add async operation
This commit is contained in:
parent
db2878034f
commit
788334d969
489
GitUtility.py
489
GitUtility.py
@ -144,7 +144,7 @@ class GitSvnSyncApp:
|
|||||||
def _setup_logging_processing(self):
|
def _setup_logging_processing(self):
|
||||||
"""Configures file logging and starts the log queue processing loop."""
|
"""Configures file logging and starts the log queue processing loop."""
|
||||||
# 1. Configure file logging only. Level determines what goes to file.
|
# 1. Configure file logging only. Level determines what goes to file.
|
||||||
setup_file_logging(level=logging.INFO)
|
setup_file_logging(level=logging.DEBUG)
|
||||||
|
|
||||||
# 2. Start the log queue polling loop if GUI widget exists
|
# 2. Start the log queue polling loop if GUI widget exists
|
||||||
if hasattr(self, "main_frame") and hasattr(self.main_frame, "log_text"):
|
if hasattr(self, "main_frame") and hasattr(self.main_frame, "log_text"):
|
||||||
@ -569,104 +569,71 @@ class GitSvnSyncApp:
|
|||||||
log_handler.log_debug("Folder browse cancelled.", func_name="browse_folder")
|
log_handler.log_debug("Folder browse cancelled.", func_name="browse_folder")
|
||||||
|
|
||||||
def update_svn_status_indicator(self, svn_path):
|
def update_svn_status_indicator(self, svn_path):
|
||||||
# ... (Logica invariata, usa log_handler.log_debug internamente se necessario) ...
|
"""
|
||||||
|
Checks repo status, updates GUI indicator, and enables/disables
|
||||||
|
relevant action widgets (Synchronous update of widget states).
|
||||||
|
"""
|
||||||
is_valid_dir = bool(svn_path and os.path.isdir(svn_path))
|
is_valid_dir = bool(svn_path and os.path.isdir(svn_path))
|
||||||
is_repo_ready = is_valid_dir and os.path.exists(os.path.join(svn_path, ".git"))
|
is_repo_ready = is_valid_dir and os.path.exists(os.path.join(svn_path, ".git"))
|
||||||
log_handler.log_debug(
|
log_handler.log_debug(f"Updating status indicator. Path='{svn_path}', Valid={is_valid_dir}, Ready={is_repo_ready}", func_name="update_svn_status_indicator")
|
||||||
f"Updating status indicator. Path='{svn_path}', Valid={is_valid_dir}, Ready={is_repo_ready}",
|
|
||||||
func_name="update_svn_status_indicator",
|
if not hasattr(self, "main_frame") or not self.main_frame.winfo_exists(): return
|
||||||
)
|
|
||||||
if not hasattr(self, "main_frame") or not self.main_frame.winfo_exists():
|
|
||||||
return
|
|
||||||
mf = self.main_frame
|
mf = self.main_frame
|
||||||
mf.update_svn_indicator(is_repo_ready)
|
mf.update_svn_indicator(is_repo_ready) # Update color/tooltip
|
||||||
|
|
||||||
|
# --- Determine Widget States ---
|
||||||
repo_ready_state = tk.NORMAL if is_repo_ready else tk.DISABLED
|
repo_ready_state = tk.NORMAL if is_repo_ready else tk.DISABLED
|
||||||
valid_path_state = tk.NORMAL if is_valid_dir else tk.DISABLED
|
valid_path_state = tk.NORMAL if is_valid_dir else tk.DISABLED
|
||||||
prepare_state = tk.NORMAL if is_valid_dir and not is_repo_ready else tk.DISABLED
|
prepare_state = tk.NORMAL if is_valid_dir and not is_repo_ready else tk.DISABLED
|
||||||
fetch_button_state = tk.DISABLED # Logic per abilitare fetch... (invariato)
|
# ... (logica fetch_button_state invariata) ...
|
||||||
try:
|
fetch_button_state = tk.DISABLED
|
||||||
svn_path_str = mf.svn_path_entry.get().strip()
|
try: # Logic per fetch button
|
||||||
usb_path_str = mf.usb_path_entry.get().strip()
|
svn_path_str=mf.svn_path_entry.get().strip(); usb_path_str=mf.usb_path_entry.get().strip(); bundle_fetch_name=mf.bundle_updated_name_entry.get().strip()
|
||||||
bundle_fetch_name = mf.bundle_updated_name_entry.get().strip()
|
can_use_svn_dir=False
|
||||||
can_use_svn_dir = False
|
|
||||||
if os.path.isdir(svn_path_str):
|
if os.path.isdir(svn_path_str):
|
||||||
if not os.listdir(svn_path_str):
|
if not os.listdir(svn_path_str): can_use_svn_dir=True
|
||||||
can_use_svn_dir = True
|
elif svn_path_str: parent_dir=os.path.dirname(svn_path_str); can_use_svn_dir=(parent_dir and os.path.isdir(parent_dir)) or (not parent_dir)
|
||||||
elif svn_path_str:
|
is_valid_usb_dir=os.path.isdir(usb_path_str); has_bundle_name=bool(bundle_fetch_name); bundle_file_exists=False
|
||||||
parent_dir = os.path.dirname(svn_path_str)
|
if is_valid_usb_dir and has_bundle_name: bundle_full_path=os.path.join(usb_path_str, bundle_fetch_name); bundle_file_exists=os.path.isfile(bundle_full_path)
|
||||||
can_use_svn_dir = (parent_dir and os.path.isdir(parent_dir)) or (
|
if is_repo_ready or (can_use_svn_dir and bundle_file_exists): fetch_button_state = tk.NORMAL
|
||||||
not parent_dir
|
except Exception as e: log_handler.log_error(f"Error checking fetch state: {e}",func_name="update_svn_status_indicator"); fetch_button_state=tk.DISABLED
|
||||||
)
|
|
||||||
is_valid_usb_dir = os.path.isdir(usb_path_str)
|
# --- Update Widget States ---
|
||||||
has_bundle_name = bool(bundle_fetch_name)
|
try:
|
||||||
bundle_file_exists = False
|
# ... (Aggiorna tutti gli altri widget come prima) ...
|
||||||
if is_valid_usb_dir and has_bundle_name:
|
if hasattr(mf,"prepare_svn_button"): mf.prepare_svn_button.config(state=prepare_state)
|
||||||
bundle_full_path = os.path.join(usb_path_str, bundle_fetch_name)
|
if hasattr(mf,"create_bundle_button"): mf.create_bundle_button.config(state=repo_ready_state)
|
||||||
bundle_file_exists = os.path.isfile(bundle_full_path)
|
if hasattr(mf,"fetch_bundle_button"): mf.fetch_bundle_button.config(state=fetch_button_state)
|
||||||
if is_repo_ready or (can_use_svn_dir and bundle_file_exists):
|
# ... etc per tutti gli altri widget ...
|
||||||
fetch_button_state = tk.NORMAL
|
|
||||||
except Exception as e:
|
# <<< MODIFICA: Non cancellare la lista changes qui se repo è pronto >>>
|
||||||
log_handler.log_error(
|
|
||||||
f"Error checking fetch button state: {e}",
|
|
||||||
func_name="update_svn_status_indicator",
|
|
||||||
)
|
|
||||||
fetch_button_state = tk.DISABLED
|
|
||||||
try: # Aggiornamento stati widget (invariato)
|
|
||||||
if hasattr(mf, "prepare_svn_button"):
|
|
||||||
mf.prepare_svn_button.config(state=prepare_state)
|
|
||||||
if hasattr(mf, "create_bundle_button"):
|
|
||||||
mf.create_bundle_button.config(state=repo_ready_state)
|
|
||||||
if hasattr(mf, "fetch_bundle_button"):
|
|
||||||
mf.fetch_bundle_button.config(state=fetch_button_state)
|
|
||||||
if hasattr(mf, "edit_gitignore_button"):
|
|
||||||
mf.edit_gitignore_button.config(state=repo_ready_state)
|
|
||||||
if hasattr(mf, "manual_backup_button"):
|
|
||||||
mf.manual_backup_button.config(state=valid_path_state)
|
|
||||||
if hasattr(mf, "autocommit_checkbox"):
|
|
||||||
mf.autocommit_checkbox.config(state=repo_ready_state)
|
|
||||||
if hasattr(mf, "commit_message_text"):
|
|
||||||
mf.commit_message_text.config(state=repo_ready_state)
|
|
||||||
if hasattr(mf, "commit_button"):
|
|
||||||
mf.commit_button.config(state=repo_ready_state)
|
|
||||||
if hasattr(mf, "refresh_changes_button"):
|
|
||||||
mf.refresh_changes_button.config(state=repo_ready_state)
|
|
||||||
if hasattr(mf, "changed_files_listbox"):
|
if hasattr(mf, "changed_files_listbox"):
|
||||||
if repo_ready_state == tk.DISABLED:
|
# Cancella la lista SOLO se il repo NON è pronto.
|
||||||
mf.update_changed_files_list(["(Repo not ready)"])
|
# Se è pronto, lascia che sia refresh_changed_files_list a popolarla.
|
||||||
widgets_require_ready = [
|
if repo_ready_state == tk.DISABLED:
|
||||||
mf.refresh_tags_button,
|
log_handler.log_debug("Repo not ready, clearing changes list via status update.", func_name="update_svn_status_indicator")
|
||||||
mf.create_tag_button,
|
mf.update_changed_files_list(["(Repository not ready)"])
|
||||||
mf.checkout_tag_button,
|
# else: Non fare nulla qui se repo è pronto
|
||||||
mf.refresh_branches_button,
|
# <<< FINE MODIFICA >>>
|
||||||
mf.create_branch_button,
|
|
||||||
mf.checkout_branch_button,
|
# ... (Aggiorna altri widget come prima) ...
|
||||||
mf.refresh_history_button,
|
widgets_require_ready=[mf.refresh_tags_button, mf.create_tag_button, mf.checkout_tag_button, mf.refresh_branches_button, mf.create_branch_button, mf.checkout_branch_button, mf.refresh_history_button, mf.history_branch_filter_combo, mf.history_text, mf.tag_listbox, mf.branch_listbox, mf.refresh_changes_button, mf.commit_button, mf.autocommit_checkbox, mf.commit_message_text, mf.edit_gitignore_button] # Lista aggiornata
|
||||||
mf.history_branch_filter_combo,
|
|
||||||
mf.history_text,
|
|
||||||
mf.tag_listbox,
|
|
||||||
mf.branch_listbox,
|
|
||||||
]
|
|
||||||
for widget in widgets_require_ready:
|
for widget in widgets_require_ready:
|
||||||
name_attr = getattr(widget, "winfo_name", None)
|
name_attr=getattr(widget,'winfo_name',None)
|
||||||
if name_attr and hasattr(mf, name_attr()):
|
if name_attr and hasattr(mf,name_attr()):
|
||||||
target = getattr(mf, name_attr())
|
target=getattr(mf,name_attr())
|
||||||
if target and target.winfo_exists():
|
if target and target.winfo_exists():
|
||||||
state = repo_ready_state
|
state=repo_ready_state;
|
||||||
if isinstance(target, ttk.Combobox):
|
try:
|
||||||
target.config(
|
if isinstance(target,ttk.Combobox): target.config(state="readonly" if state==tk.NORMAL else tk.DISABLED)
|
||||||
state="readonly" if state == tk.NORMAL else tk.DISABLED
|
elif isinstance(target,(tk.Text,scrolledtext.ScrolledText)): target.config(state=state)
|
||||||
)
|
elif isinstance(target,tk.Listbox): target.config(state=state)
|
||||||
elif isinstance(target, (tk.Text, scrolledtext.ScrolledText)):
|
else: target.config(state=state)
|
||||||
target.config(state=state)
|
except tk.TclError: pass # Ignora errori Tcl rari
|
||||||
elif isinstance(target, tk.Listbox):
|
|
||||||
target.config(state=state)
|
|
||||||
else:
|
|
||||||
target.config(state=state)
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
log_handler.log_error(
|
log_handler.log_error(f"Error updating widget states: {e}", func_name="update_svn_status_indicator")
|
||||||
f"Error updating widget states: {e}",
|
|
||||||
func_name="update_svn_status_indicator",
|
|
||||||
)
|
|
||||||
|
|
||||||
def _is_repo_ready(self, repo_path):
|
def _is_repo_ready(self, repo_path):
|
||||||
return bool(
|
return bool(
|
||||||
@ -807,54 +774,36 @@ class GitSvnSyncApp:
|
|||||||
# --- ==== ASYNCHRONOUS ACTION IMPLEMENTATIONS ==== ---
|
# --- ==== ASYNCHRONOUS ACTION IMPLEMENTATIONS ==== ---
|
||||||
|
|
||||||
def _start_async_operation(self, worker_func, args_tuple, context_dict):
|
def _start_async_operation(self, worker_func, args_tuple, context_dict):
|
||||||
"""Generic helper to start an async operation."""
|
"""Generic helper to start an async operation with UI feedback."""
|
||||||
|
# ... (controllo main_frame esistente) ...
|
||||||
if not hasattr(self, "main_frame") or not self.main_frame.winfo_exists():
|
if not hasattr(self, "main_frame") or not self.main_frame.winfo_exists():
|
||||||
log_handler.log_error(
|
log_handler.log_error("Cannot start async op: Main frame missing.", func_name="_start_async_operation")
|
||||||
"Cannot start async operation: Main frame not available.",
|
|
||||||
func_name="_start_async_operation",
|
|
||||||
)
|
|
||||||
return
|
return
|
||||||
|
|
||||||
context_name = context_dict.get("context", "unknown_op")
|
context_name = context_dict.get('context', 'unknown_op')
|
||||||
log_handler.log_info(
|
status_msg = context_dict.get('status_msg', context_name)
|
||||||
f"--- Action Triggered: {context_name} (Async Queue) ---",
|
log_handler.log_info(f"--- Action Triggered: {context_name} (Async Queue) ---", func_name=context_name)
|
||||||
func_name=context_name,
|
|
||||||
) # Usa contesto per log
|
|
||||||
|
|
||||||
# Disable UI
|
# --- Update UI: Disable widgets and set PROCESSING status ---
|
||||||
self.main_frame.set_action_widgets_state(tk.DISABLED)
|
self.main_frame.set_action_widgets_state(tk.DISABLED)
|
||||||
self.main_frame.update_status_bar(
|
# <<< MODIFICA: Imposta colore giallo per "in corso" >>>
|
||||||
f"Processing: {context_dict.get('status_msg', context_name)}..."
|
self.main_frame.update_status_bar(f"Processing: {status_msg}...", bg_color=self.main_frame.STATUS_YELLOW)
|
||||||
)
|
|
||||||
results_queue = queue.Queue(maxsize=1)
|
results_queue = queue.Queue(maxsize=1)
|
||||||
|
|
||||||
# Prepend results_queue to args for the worker
|
# --- Start Worker Thread ---
|
||||||
full_args = args_tuple + (results_queue,)
|
full_args = args_tuple + (results_queue,)
|
||||||
|
log_handler.log_debug(f"Creating worker thread for {context_name}.", func_name="_start_async_operation")
|
||||||
# Create and start thread
|
worker_thread = threading.Thread(target=worker_func, args=full_args, daemon=True)
|
||||||
log_handler.log_debug(
|
log_handler.log_debug(f"Starting worker thread for {context_name}.", func_name="_start_async_operation")
|
||||||
f"Creating worker thread for {context_name}.",
|
|
||||||
func_name="_start_async_operation",
|
|
||||||
)
|
|
||||||
worker_thread = threading.Thread(
|
|
||||||
target=worker_func, args=full_args, daemon=True
|
|
||||||
)
|
|
||||||
log_handler.log_debug(
|
|
||||||
f"Starting worker thread for {context_name}.",
|
|
||||||
func_name="_start_async_operation",
|
|
||||||
)
|
|
||||||
worker_thread.start()
|
worker_thread.start()
|
||||||
|
|
||||||
# Schedule completion check
|
# --- Schedule Completion Check ---
|
||||||
log_handler.log_debug(
|
log_handler.log_debug(f"Scheduling completion check for {context_name}.", func_name="_start_async_operation")
|
||||||
f"Scheduling completion check for {context_name}.",
|
|
||||||
func_name="_start_async_operation",
|
|
||||||
)
|
|
||||||
self.master.after(
|
self.master.after(
|
||||||
self.ASYNC_QUEUE_CHECK_INTERVAL_MS,
|
self.ASYNC_QUEUE_CHECK_INTERVAL_MS,
|
||||||
self._check_completion_queue,
|
self._check_completion_queue,
|
||||||
results_queue,
|
results_queue,
|
||||||
context_dict, # Pass the whole context dict
|
context_dict
|
||||||
)
|
)
|
||||||
|
|
||||||
# --- Specific Action Wrappers ---
|
# --- Specific Action Wrappers ---
|
||||||
@ -1468,20 +1417,25 @@ class GitSvnSyncApp:
|
|||||||
)
|
)
|
||||||
|
|
||||||
def _run_refresh_changes_async(self, svn_path, results_queue):
|
def _run_refresh_changes_async(self, svn_path, results_queue):
|
||||||
log_handler.log_debug(
|
func_name = "_run_refresh_changes_async"
|
||||||
"[Worker] Started: Refresh Changes", func_name="_run_refresh_changes_async"
|
log_handler.log_debug("[Worker] Started: Refresh Changes", func_name=func_name)
|
||||||
)
|
files_status_list = ["(Worker Error Default)"]
|
||||||
try:
|
try:
|
||||||
|
log_handler.log_debug("[Worker] Calling git_commands.get_status_short...", func_name=func_name)
|
||||||
files_status_list = self.git_commands.get_status_short(svn_path)
|
files_status_list = self.git_commands.get_status_short(svn_path)
|
||||||
|
|
||||||
|
# <<< NUOVO LOG >>>
|
||||||
|
log_handler.log_debug(f"[Worker] Received list from get_status_short: {files_status_list}", func_name=func_name)
|
||||||
|
# <<< FINE NUOVO LOG >>>
|
||||||
|
|
||||||
count = len(files_status_list)
|
count = len(files_status_list)
|
||||||
message = (
|
log_handler.log_info(f"[Worker] Found {count} changes.", func_name=func_name)
|
||||||
f"Ready ({count} changes detected)."
|
message = f"Ready ({count} changes detected)." if count > 0 else "Ready (No changes detected)."
|
||||||
if count > 0
|
log_handler.log_debug(f"[Worker] Preparing to put result in queue. Data: status='success', count={count}", func_name=func_name)
|
||||||
else "Ready (No changes detected)."
|
result_dict = {'status': 'success', 'result': files_status_list, 'message': message, 'context': 'refresh_changes'}
|
||||||
)
|
log_handler.log_debug(f"[Worker] Data prepared: {result_dict}", func_name=func_name)
|
||||||
results_queue.put(
|
results_queue.put(result_dict)
|
||||||
{"status": "success", "result": files_status_list, "message": message}
|
log_handler.log_debug("[Worker] Successfully PUT result in queue.", func_name=func_name)
|
||||||
)
|
|
||||||
except (GitCommandError, ValueError) as e:
|
except (GitCommandError, ValueError) as e:
|
||||||
log_handler.log_exception(
|
log_handler.log_exception(
|
||||||
f"[Worker] EXCEPTION: {e}", func_name="_run_refresh_changes_async"
|
f"[Worker] EXCEPTION: {e}", func_name="_run_refresh_changes_async"
|
||||||
@ -1507,9 +1461,7 @@ class GitSvnSyncApp:
|
|||||||
"message": "Unexpected error refreshing changes.",
|
"message": "Unexpected error refreshing changes.",
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
log_handler.log_debug(
|
log_handler.log_debug(f"[Worker] Reached end of function.", func_name=func_name)
|
||||||
"[Worker] Finished: Refresh Changes", func_name="_run_refresh_changes_async"
|
|
||||||
)
|
|
||||||
|
|
||||||
def _run_prepare_async(self, svn_path, results_queue):
|
def _run_prepare_async(self, svn_path, results_queue):
|
||||||
log_handler.log_debug(
|
log_handler.log_debug(
|
||||||
@ -1880,211 +1832,124 @@ class GitSvnSyncApp:
|
|||||||
# --- ==== Gestione Coda Risultati ==== ---
|
# --- ==== Gestione Coda Risultati ==== ---
|
||||||
|
|
||||||
def _check_completion_queue(self, results_queue, context):
|
def _check_completion_queue(self, results_queue, context):
|
||||||
"""Checks operation result queue and updates GUI. Runs in main thread."""
|
"""Checks result queue, updates GUI (incl. status bar color)."""
|
||||||
task_context = context.get("context", "unknown")
|
task_context = context.get('context', 'unknown')
|
||||||
log_handler.log_debug(
|
# log_handler.log_debug(f"Checking completion queue for context: {task_context}", func_name="_check_completion_queue") # Mantenuto commentato per ora
|
||||||
f"Checking completion queue for context: {task_context}",
|
|
||||||
func_name="_check_completion_queue",
|
|
||||||
)
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
result_data = results_queue.get_nowait()
|
result_data = results_queue.get_nowait()
|
||||||
log_handler.log_info(
|
log_handler.log_info(f"Result received for '{task_context}'. Status: {result_data.get('status')}", func_name="_check_completion_queue")
|
||||||
f"Result received for '{task_context}'. Status: {result_data.get('status')}",
|
|
||||||
func_name="_check_completion_queue",
|
|
||||||
)
|
|
||||||
|
|
||||||
# --- 1. Re-enable GUI ---
|
# --- 1. Re-enable GUI Widgets ---
|
||||||
log_handler.log_debug(
|
log_handler.log_debug("Re-enabling widgets.", func_name="_check_completion_queue")
|
||||||
"Re-enabling widgets.", func_name="_check_completion_queue"
|
|
||||||
)
|
|
||||||
self.main_frame.set_action_widgets_state(tk.NORMAL)
|
self.main_frame.set_action_widgets_state(tk.NORMAL)
|
||||||
|
|
||||||
# --- 2. Extract Details ---
|
# --- 2. Extract Details ---
|
||||||
status = result_data.get("status")
|
status = result_data.get('status')
|
||||||
message = result_data.get("message")
|
message = result_data.get('message')
|
||||||
result = result_data.get("result")
|
result = result_data.get('result')
|
||||||
exception = result_data.get("exception")
|
exception = result_data.get('exception')
|
||||||
committed = result_data.get("committed", False)
|
committed = result_data.get('committed', False)
|
||||||
is_conflict = result_data.get("conflict", False)
|
is_conflict = result_data.get('conflict', False)
|
||||||
repo_path_conflict = result_data.get("repo_path")
|
repo_path_conflict = result_data.get('repo_path')
|
||||||
new_branch_context = context.get("new_branch_name") # From original context
|
new_branch_context = context.get('new_branch_name')
|
||||||
|
|
||||||
|
# --- 3. Update Status Bar (con colore e reset temporizzato) ---
|
||||||
|
# (Logica status bar invariata)
|
||||||
|
status_color = None
|
||||||
|
reset_duration = 5000 # Resetta colore dopo 5 secondi
|
||||||
|
if status == 'success':
|
||||||
|
status_color = self.main_frame.STATUS_GREEN
|
||||||
|
elif status == 'warning':
|
||||||
|
status_color = self.main_frame.STATUS_YELLOW
|
||||||
|
reset_duration = 7000
|
||||||
|
elif status == 'error':
|
||||||
|
status_color = self.main_frame.STATUS_RED
|
||||||
|
reset_duration = 10000
|
||||||
|
|
||||||
|
self.main_frame.update_status_bar(message, bg_color=status_color, duration_ms=reset_duration)
|
||||||
|
|
||||||
# --- 3. Update Status Bar ---
|
|
||||||
self.main_frame.update_status_bar(message)
|
|
||||||
|
|
||||||
# --- 4. Process Result & Trigger Updates ---
|
# --- 4. Process Result & Trigger Updates ---
|
||||||
repo_path_for_updates = self._get_and_validate_svn_path(
|
repo_path_for_updates = self._get_and_validate_svn_path("Post-Action Update")
|
||||||
"Post-Action Update"
|
|
||||||
) # Get current path
|
|
||||||
|
|
||||||
if status == "success":
|
if status == 'success':
|
||||||
# Show popups for major actions
|
|
||||||
if task_context in [
|
|
||||||
"prepare_repo",
|
|
||||||
"create_bundle",
|
|
||||||
"fetch_bundle",
|
|
||||||
"commit",
|
|
||||||
"create_tag",
|
|
||||||
"checkout_tag",
|
|
||||||
"create_branch",
|
|
||||||
"checkout_branch",
|
|
||||||
"manual_backup",
|
|
||||||
"_handle_gitignore_save_async",
|
|
||||||
]:
|
|
||||||
if task_context == "create_bundle" and not result:
|
|
||||||
self.main_frame.show_warning(
|
|
||||||
"Info", message
|
|
||||||
) # No bundle file warning
|
|
||||||
elif task_context == "commit" and not committed:
|
|
||||||
self.main_frame.show_info("Info", message) # No changes info
|
|
||||||
elif task_context == "manual_backup" and not result:
|
|
||||||
self.main_frame.show_warning(
|
|
||||||
"Info", message
|
|
||||||
) # No backup file warning
|
|
||||||
elif (
|
|
||||||
task_context == "_handle_gitignore_save_async" and not committed
|
|
||||||
):
|
|
||||||
pass # No popup if untrack didn't commit
|
|
||||||
else:
|
|
||||||
self.main_frame.show_info(
|
|
||||||
"Success", message
|
|
||||||
) # Generic success popup
|
|
||||||
|
|
||||||
# Determine which async refreshes to trigger
|
|
||||||
refresh_list = []
|
refresh_list = []
|
||||||
if task_context in [
|
# (Logica per popolare refresh_list invariata)
|
||||||
"prepare_repo",
|
if task_context in ['prepare_repo', 'fetch_bundle', 'commit', 'create_tag', 'checkout_tag', 'create_branch', 'checkout_branch', '_handle_gitignore_save_async', 'add_file']:
|
||||||
"fetch_bundle",
|
if committed or task_context in ['fetch_bundle','prepare_repo','create_tag','_handle_gitignore_save_async']: refresh_list.append(self.refresh_commit_history)
|
||||||
"commit",
|
if task_context != 'refresh_changes': refresh_list.append(self.refresh_changed_files_list)
|
||||||
"create_tag",
|
if task_context not in ['refresh_tags','checkout_tag'] or committed: refresh_list.append(self.refresh_tag_list)
|
||||||
"checkout_tag",
|
if task_context != 'refresh_branches': refresh_list.append(self.refresh_branch_list)
|
||||||
"create_branch",
|
|
||||||
"checkout_branch",
|
|
||||||
"_handle_gitignore_save_async",
|
# Gestione aggiornamenti diretti post-refresh
|
||||||
"add_file",
|
if task_context == 'refresh_tags':
|
||||||
]:
|
|
||||||
if committed or task_context in [
|
|
||||||
"fetch_bundle",
|
|
||||||
"prepare_repo",
|
|
||||||
"create_tag",
|
|
||||||
"_handle_gitignore_save_async",
|
|
||||||
]:
|
|
||||||
refresh_list.append(self.refresh_commit_history)
|
|
||||||
if task_context != "refresh_changes":
|
|
||||||
refresh_list.append(
|
|
||||||
self.refresh_changed_files_list
|
|
||||||
) # Always refresh changes unless it WAS the refresh
|
|
||||||
if (
|
|
||||||
task_context not in ["refresh_tags", "checkout_tag"]
|
|
||||||
or committed
|
|
||||||
):
|
|
||||||
refresh_list.append(
|
|
||||||
self.refresh_tag_list
|
|
||||||
) # Refresh tags if commit or fetch happened
|
|
||||||
if task_context != "refresh_branches":
|
|
||||||
refresh_list.append(
|
|
||||||
self.refresh_branch_list
|
|
||||||
) # Always refresh branches after action
|
|
||||||
elif task_context == "refresh_tags":
|
|
||||||
self.main_frame.update_tag_list(result if result else [])
|
self.main_frame.update_tag_list(result if result else [])
|
||||||
elif task_context == "refresh_branches":
|
elif task_context == 'refresh_branches':
|
||||||
branches, current = result if result else ([], None)
|
branches, current = result if result else ([], None)
|
||||||
self.main_frame.update_branch_list(branches, current)
|
self.main_frame.update_branch_list(branches, current)
|
||||||
if hasattr(self.main_frame, "update_history_branch_filter"):
|
self.main_frame.update_history_branch_filter(branches)
|
||||||
self.main_frame.update_history_branch_filter(
|
elif task_context == 'refresh_history':
|
||||||
[b for b in branches if not b.startswith("(")] or [],
|
|
||||||
current,
|
|
||||||
)
|
|
||||||
elif task_context == "refresh_history":
|
|
||||||
self.main_frame.update_history_display(result if result else [])
|
self.main_frame.update_history_display(result if result else [])
|
||||||
elif task_context == "refresh_changes":
|
elif task_context == 'refresh_changes':
|
||||||
|
# ---<<< INIZIO MODIFICA DEBUG >>>---
|
||||||
|
# Logga esattamente cosa sta per essere passato a update_changed_files_list
|
||||||
|
log_handler.log_debug(
|
||||||
|
f"Preparing to call update_changed_files_list. "
|
||||||
|
f"Task Context: '{task_context}'. Result type: {type(result)}. Result value: {repr(result)}",
|
||||||
|
func_name="_check_completion_queue"
|
||||||
|
)
|
||||||
|
# ---<<< FINE MODIFICA DEBUG >>>---
|
||||||
self.main_frame.update_changed_files_list(result if result else [])
|
self.main_frame.update_changed_files_list(result if result else [])
|
||||||
|
|
||||||
# Clear commit message if appropriate
|
# (Altre gestioni di successo invariate: commit, create_branch checkout, etc.)
|
||||||
if task_context == "commit" and committed:
|
if task_context == 'commit' and committed: self.main_frame.clear_commit_message()
|
||||||
self.main_frame.clear_commit_message()
|
if task_context == 'create_branch' and new_branch_context:
|
||||||
|
if self.main_frame.ask_yes_no("Checkout?", f"Switch to new branch '{new_branch_context}'?"):
|
||||||
|
self.checkout_branch(branch_to_checkout=new_branch_context)
|
||||||
|
else: # Refresh history if not checking out
|
||||||
|
if self.refresh_commit_history not in refresh_list: refresh_list.append(self.refresh_commit_history)
|
||||||
|
|
||||||
# Ask to checkout new branch
|
|
||||||
if task_context == "create_branch" and new_branch_context:
|
|
||||||
if self.main_frame.ask_yes_no(
|
|
||||||
"Checkout?", f"Switch to new branch '{new_branch_context}'?"
|
|
||||||
):
|
|
||||||
self.checkout_branch(
|
|
||||||
branch_to_checkout=new_branch_context
|
|
||||||
) # Triggers another async op
|
|
||||||
else: # Refresh history if not checking out
|
|
||||||
if self.refresh_commit_history not in refresh_list:
|
|
||||||
refresh_list.append(self.refresh_commit_history)
|
|
||||||
|
|
||||||
# Trigger the collected refreshes (which are async)
|
# Trigger collected async refreshes
|
||||||
if repo_path_for_updates: # Need path to refresh
|
if repo_path_for_updates and refresh_list:
|
||||||
log_handler.log_debug(
|
log_handler.log_debug(f"Triggering {len(refresh_list)} async refreshes for '{task_context}'", func_name="_check_completion_queue")
|
||||||
f"Triggering {len(refresh_list)} async refreshes for context '{task_context}'",
|
|
||||||
func_name="_check_completion_queue",
|
|
||||||
)
|
|
||||||
for refresh_func in refresh_list:
|
for refresh_func in refresh_list:
|
||||||
try:
|
try: refresh_func()
|
||||||
refresh_func()
|
except Exception as ref_e: log_handler.log_error(f"Error triggering {refresh_func.__name__}: {ref_e}", func_name="_check_completion_queue")
|
||||||
except Exception as ref_e:
|
|
||||||
log_handler.log_error(
|
|
||||||
f"Error triggering refresh {refresh_func.__name__}: {ref_e}",
|
|
||||||
func_name="_check_completion_queue",
|
|
||||||
)
|
|
||||||
elif refresh_list:
|
elif refresh_list:
|
||||||
log_handler.log_warning(
|
log_handler.log_warning("Cannot trigger UI refreshes: Repo path unavailable.", func_name="_check_completion_queue")
|
||||||
"Cannot trigger UI refreshes: Repo path unavailable.",
|
|
||||||
func_name="_check_completion_queue",
|
|
||||||
)
|
|
||||||
|
|
||||||
elif status == "warning":
|
|
||||||
self.main_frame.show_warning("Operation Info", message)
|
|
||||||
if "already prepared" in message:
|
|
||||||
self.refresh_changed_files_list() # Async refresh
|
|
||||||
|
|
||||||
elif status == "error":
|
elif status == 'warning':
|
||||||
|
# (gestione warning invariata)
|
||||||
|
self.main_frame.show_warning("Operation Info", message)
|
||||||
|
if "already prepared" in message: self.refresh_changed_files_list()
|
||||||
|
|
||||||
|
elif status == 'error':
|
||||||
|
# (gestione errore invariata)
|
||||||
error_details = f"{message}\n({exception})" if exception else message
|
error_details = f"{message}\n({exception})" if exception else message
|
||||||
if is_conflict and repo_path_conflict:
|
if is_conflict and repo_path_conflict: self.main_frame.show_error("Merge Conflict", f"Conflict occurred.\nResolve in:\n{repo_path_conflict}\nThen commit.")
|
||||||
self.main_frame.show_error(
|
elif "Uncommitted changes" in message: self.main_frame.show_warning("Action Blocked", f"{exception}\nCommit or stash first.")
|
||||||
"Merge Conflict",
|
else: self.main_frame.show_error("Error: Operation Failed", error_details)
|
||||||
f"Conflict occurred.\nResolve in:\n{repo_path_conflict}\nThen commit.",
|
# Aggiornamento liste con errore (opzionale, dipende dal task)
|
||||||
)
|
if task_context == 'refresh_tags': self.main_frame.update_tag_list([("(Error)", "")])
|
||||||
elif "Uncommitted changes" in message:
|
elif task_context == 'refresh_branches': self.main_frame.update_branch_list([], None); self.main_frame.update_history_branch_filter([])
|
||||||
self.main_frame.show_warning(
|
elif task_context == 'refresh_history': self.main_frame.update_history_display(["(Error retrieving history)"])
|
||||||
"Action Blocked", f"{exception}\nCommit or stash first."
|
elif task_context == 'refresh_changes': self.main_frame.update_changed_files_list(["(Error refreshing changes)"])
|
||||||
)
|
|
||||||
else:
|
|
||||||
self.main_frame.show_error("Error: Operation Failed", error_details)
|
|
||||||
# Update lists with error messages if applicable
|
|
||||||
if task_context == "refresh_tags":
|
|
||||||
self.main_frame.update_tag_list(
|
|
||||||
result if result else [("(Error)", "")]
|
|
||||||
)
|
|
||||||
# Add similar error updates for other refresh contexts if needed
|
|
||||||
|
|
||||||
log_handler.log_debug(
|
|
||||||
f"Finished processing result for context '{task_context}'.",
|
log_handler.log_debug(f"Finished processing result for context '{task_context}'.", func_name="_check_completion_queue")
|
||||||
func_name="_check_completion_queue",
|
|
||||||
)
|
|
||||||
|
|
||||||
except queue.Empty:
|
except queue.Empty:
|
||||||
# Reschedule check
|
# Reschedule check
|
||||||
self.master.after(
|
self.master.after(self.ASYNC_QUEUE_CHECK_INTERVAL_MS, self._check_completion_queue, results_queue, context)
|
||||||
self.ASYNC_QUEUE_CHECK_INTERVAL_MS,
|
|
||||||
self._check_completion_queue,
|
|
||||||
results_queue,
|
|
||||||
context,
|
|
||||||
)
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
log_handler.log_exception(
|
log_handler.log_exception(f"Critical error processing completion queue for {task_context}: {e}", func_name="_check_completion_queue")
|
||||||
f"Critical error processing completion queue for {task_context}: {e}",
|
try: self.main_frame.set_action_widgets_state(tk.NORMAL) # Attempt recovery
|
||||||
func_name="_check_completion_queue",
|
except: pass
|
||||||
)
|
self.main_frame.update_status_bar("Error processing async result.", bg_color=self.main_frame.STATUS_RED, duration_ms=10000)
|
||||||
try:
|
|
||||||
self.main_frame.set_action_widgets_state(tk.NORMAL) # Attempt recovery
|
|
||||||
except:
|
|
||||||
pass
|
|
||||||
self.main_frame.update_status_bar("Error processing async result.")
|
|
||||||
|
|
||||||
|
|
||||||
# --- Punto di Ingresso (invariato) ---
|
# --- Punto di Ingresso (invariato) ---
|
||||||
|
|||||||
@ -997,19 +997,26 @@ class GitCommands:
|
|||||||
|
|
||||||
def get_status_short(self, working_directory: str):
|
def get_status_short(self, working_directory: str):
|
||||||
func_name = "get_status_short"
|
func_name = "get_status_short"
|
||||||
log_handler.log_debug(
|
log_handler.log_debug(f"Getting short status for '{working_directory}' (-z)", func_name=func_name)
|
||||||
f"Getting short status for '{working_directory}'", func_name=func_name
|
|
||||||
)
|
|
||||||
cmd = ["git", "status", "--short", "-z", "--ignored=no"]
|
cmd = ["git", "status", "--short", "-z", "--ignored=no"]
|
||||||
try:
|
try:
|
||||||
result = self.log_and_execute(
|
result = self.log_and_execute(cmd, working_directory, check=True, log_output_level=logging.DEBUG)
|
||||||
cmd, working_directory, check=True, log_output_level=logging.DEBUG
|
|
||||||
)
|
# <<< MODIFICA/VERIFICA >>>
|
||||||
lines = [line for line in result.stdout.split("\0") if line]
|
raw_output = result.stdout
|
||||||
log_handler.log_info(
|
log_handler.log_debug(f"Raw stdout length: {len(raw_output)}", func_name=func_name)
|
||||||
f"Status check returned {len(lines)} items.", func_name=func_name
|
# Logga la rappresentazione repr() che mostra caratteri speciali come \x00
|
||||||
)
|
log_handler.log_debug(f"Raw stdout repr: {repr(raw_output)}", func_name=func_name)
|
||||||
return lines
|
|
||||||
|
# Esegui lo split e verifica
|
||||||
|
status_lines = [line for line in raw_output.split('\0') if line] # Filtra stringhe vuote
|
||||||
|
log_handler.log_debug(f"Split resulted in {len(status_lines)} non-empty lines.", func_name=func_name)
|
||||||
|
# Logga la lista risultante per conferma
|
||||||
|
log_handler.log_debug(f"Split lines list: {status_lines}", func_name=func_name)
|
||||||
|
# <<< FINE MODIFICA/VERIFICA >>>
|
||||||
|
|
||||||
|
log_handler.log_info(f"Status check returned {len(status_lines)} items.", func_name=func_name)
|
||||||
|
return status_lines
|
||||||
except GitCommandError as e:
|
except GitCommandError as e:
|
||||||
log_handler.log_error(f"Failed get status: {e}", func_name=func_name)
|
log_handler.log_error(f"Failed get status: {e}", func_name=func_name)
|
||||||
return []
|
return []
|
||||||
|
|||||||
142
gui.py
142
gui.py
@ -408,6 +408,11 @@ class MainFrame(ttk.Frame):
|
|||||||
|
|
||||||
GREEN = "#90EE90"
|
GREEN = "#90EE90"
|
||||||
RED = "#F08080" # Color constants
|
RED = "#F08080" # Color constants
|
||||||
|
# Aggiungi colori per status bar
|
||||||
|
STATUS_YELLOW = "#FFFACD" # Lemon Chiffon (giallo chiaro)
|
||||||
|
STATUS_RED = "#FFA07A" # Light Salmon (rosso/arancio chiaro)
|
||||||
|
STATUS_GREEN = "#98FB98" # Pale Green (verde chiaro)
|
||||||
|
STATUS_DEFAULT_BG = None # Per ripristinare il colore default del tema
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
@ -523,6 +528,17 @@ class MainFrame(ttk.Frame):
|
|||||||
padding=(5, 2),
|
padding=(5, 2),
|
||||||
)
|
)
|
||||||
self.status_bar.pack(side=tk.BOTTOM, fill=tk.X, pady=(2, 0), padx=0)
|
self.status_bar.pack(side=tk.BOTTOM, fill=tk.X, pady=(2, 0), padx=0)
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Usa lookup per ottenere il colore di sfondo standard di un TTK Label
|
||||||
|
s = ttk.Style()
|
||||||
|
self.STATUS_DEFAULT_BG = s.lookup('TLabel', 'background')
|
||||||
|
except tk.TclError:
|
||||||
|
# Fallback se il tema non è pronto o lookup fallisce
|
||||||
|
self.STATUS_DEFAULT_BG = self.status_bar.cget('background') # Usa colore attuale widget
|
||||||
|
self.status_bar_var.set("Initializing...")
|
||||||
|
|
||||||
|
self._status_reset_timer = None
|
||||||
|
|
||||||
# --- Initial State ---
|
# --- Initial State ---
|
||||||
self._initialize_profile_selection()
|
self._initialize_profile_selection()
|
||||||
@ -1282,37 +1298,65 @@ class MainFrame(ttk.Frame):
|
|||||||
)
|
)
|
||||||
|
|
||||||
def update_changed_files_list(self, files_status_list):
|
def update_changed_files_list(self, files_status_list):
|
||||||
if (
|
"""Clears and populates the changed files listbox, sanitizing input."""
|
||||||
not hasattr(self, "changed_files_listbox")
|
# Usa la costante Listbox invece di hasattr ogni volta
|
||||||
or not self.changed_files_listbox.winfo_exists()
|
listbox = getattr(self, "changed_files_listbox", None)
|
||||||
):
|
if not listbox or not listbox.winfo_exists():
|
||||||
|
# Logga se il widget non è disponibile (usa print come fallback qui)
|
||||||
|
print("ERROR: changed_files_listbox not available for update.", file=sys.stderr)
|
||||||
return
|
return
|
||||||
|
|
||||||
try:
|
try:
|
||||||
self.changed_files_listbox.config(state=tk.NORMAL)
|
listbox.config(state=tk.NORMAL)
|
||||||
self.changed_files_listbox.delete(0, tk.END)
|
listbox.delete(0, tk.END)
|
||||||
if files_status_list:
|
if files_status_list:
|
||||||
|
# Reset color
|
||||||
try:
|
try:
|
||||||
if self.changed_files_listbox.cget("fg") == "grey":
|
if listbox.cget("fg") == "grey":
|
||||||
self.changed_files_listbox.config(
|
listbox.config(fg=self.style.lookup("TListbox", "foreground"))
|
||||||
fg=self.style.lookup("TListbox", "foreground")
|
except tk.TclError: pass
|
||||||
)
|
|
||||||
except tk.TclError:
|
# <<< MODIFICA: Sanifica ogni riga prima di inserirla >>>
|
||||||
pass
|
processed_lines = 0
|
||||||
for line in files_status_list:
|
for status_line in files_status_list:
|
||||||
self.changed_files_listbox.insert(tk.END, line)
|
try:
|
||||||
|
# Assicura sia una stringa e rimuovi caratteri potenzialmente problematici
|
||||||
|
# (es. NUL residuo, anche se split dovrebbe averlo rimosso)
|
||||||
|
# Potremmo usare una regex più complessa per rimuovere tutti i controlli C0/C1
|
||||||
|
# ma iniziamo con una pulizia base.
|
||||||
|
sanitized_line = str(status_line).replace('\x00', '').strip()
|
||||||
|
if sanitized_line: # Inserisci solo se non vuota dopo pulizia
|
||||||
|
listbox.insert(tk.END, sanitized_line)
|
||||||
|
processed_lines += 1
|
||||||
|
else:
|
||||||
|
# Logga se una riga viene scartata
|
||||||
|
print(f"Warning: Sanitized status line resulted in empty string: {repr(status_line)}", file=sys.stderr)
|
||||||
|
except Exception as insert_err:
|
||||||
|
# Logga errore specifico per riga
|
||||||
|
print(f"ERROR inserting line into listbox: {insert_err} - Line: {repr(status_line)}", file=sys.stderr)
|
||||||
|
# Inserisci un placeholder di errore per quella riga
|
||||||
|
listbox.insert(tk.END, f"(Error processing line: {repr(status_line)})")
|
||||||
|
listbox.itemconfig(tk.END, {'fg': 'red'})
|
||||||
|
# <<< FINE MODIFICA >>>
|
||||||
|
|
||||||
|
# Se nessuna linea valida è stata processata, mostra placeholder
|
||||||
|
if processed_lines == 0 and files_status_list:
|
||||||
|
listbox.insert(tk.END, "(Error processing all lines)")
|
||||||
|
listbox.config(fg="red")
|
||||||
|
|
||||||
else:
|
else:
|
||||||
self.changed_files_listbox.insert(tk.END, "(No changes detected)")
|
listbox.insert(tk.END, "(No changes detected)")
|
||||||
self.changed_files_listbox.config(fg="grey")
|
listbox.config(fg="grey")
|
||||||
self.changed_files_listbox.config(state=tk.NORMAL)
|
|
||||||
self.changed_files_listbox.yview_moveto(0.0)
|
listbox.config(state=tk.NORMAL) # Mantieni selezionabile
|
||||||
|
listbox.yview_moveto(0.0)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"ERROR updating changes list GUI: {e}", file=sys.stderr)
|
print(f"ERROR updating changed files list GUI: {e}", file=sys.stderr)
|
||||||
try:
|
try:
|
||||||
self.changed_files_listbox.delete(0, tk.END)
|
listbox.delete(0, tk.END)
|
||||||
self.changed_files_listbox.insert(tk.END, "(Error)")
|
listbox.insert(tk.END, "(Error updating list)")
|
||||||
self.changed_files_listbox.config(fg="red")
|
listbox.config(fg="red")
|
||||||
except:
|
except: pass # Ignora errori durante la visualizzazione dell'errore
|
||||||
pass
|
|
||||||
|
|
||||||
def _on_changed_file_double_click(self, event):
|
def _on_changed_file_double_click(self, event):
|
||||||
widget = event.widget
|
widget = event.widget
|
||||||
@ -1372,13 +1416,53 @@ class MainFrame(ttk.Frame):
|
|||||||
finally:
|
finally:
|
||||||
self.changed_files_context_menu.grab_release()
|
self.changed_files_context_menu.grab_release()
|
||||||
|
|
||||||
def update_status_bar(self, message):
|
def update_status_bar(self, message, bg_color=None, duration_ms=None):
|
||||||
if hasattr(self, "status_bar_var"):
|
"""
|
||||||
|
Safely updates the status bar text and optionally its background color.
|
||||||
|
Optionally resets the color after a duration.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
message (str): The text to display.
|
||||||
|
bg_color (str | None): Background color name (e.g., "#FFFACD") or None to use default.
|
||||||
|
duration_ms (int | None): If set, reset color to default after this many ms.
|
||||||
|
"""
|
||||||
|
if hasattr(self, "status_bar_var") and hasattr(self, "status_bar"):
|
||||||
try:
|
try:
|
||||||
self.master.after(0, self.status_bar_var.set, message)
|
# Cancella eventuale timer di reset precedente
|
||||||
|
if self._status_reset_timer:
|
||||||
|
self.master.after_cancel(self._status_reset_timer)
|
||||||
|
self._status_reset_timer = None
|
||||||
|
|
||||||
|
# Funzione interna per applicare aggiornamenti (via root.after)
|
||||||
|
def _update():
|
||||||
|
if self.status_bar.winfo_exists(): # Controlla esistenza widget
|
||||||
|
self.status_bar_var.set(message)
|
||||||
|
actual_bg = bg_color if bg_color else self.STATUS_DEFAULT_BG
|
||||||
|
try:
|
||||||
|
self.status_bar.config(background=actual_bg)
|
||||||
|
except tk.TclError: # Gestisci errore se colore non valido
|
||||||
|
self.status_bar.config(background=self.STATUS_DEFAULT_BG)
|
||||||
|
print(f"Warning: Invalid status bar color '{bg_color}', using default.", file=sys.stderr)
|
||||||
|
|
||||||
|
# Pianifica reset colore se richiesto
|
||||||
|
if bg_color and duration_ms and duration_ms > 0:
|
||||||
|
self._status_reset_timer = self.master.after(duration_ms, self.reset_status_bar_color)
|
||||||
|
|
||||||
|
# Pianifica l'esecuzione nel main loop
|
||||||
|
self.master.after(0, _update)
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"ERROR updating status bar: {e}", file=sys.stderr)
|
print(f"ERROR updating status bar: {e}", file=sys.stderr)
|
||||||
|
|
||||||
|
def reset_status_bar_color(self):
|
||||||
|
"""Resets the status bar background color to the default."""
|
||||||
|
self._status_reset_timer = None # Resetta timer ID
|
||||||
|
if hasattr(self, "status_bar") and self.status_bar.winfo_exists():
|
||||||
|
try:
|
||||||
|
self.status_bar.config(background=self.STATUS_DEFAULT_BG)
|
||||||
|
except Exception as e:
|
||||||
|
print(f"ERROR resetting status bar color: {e}", file=sys.stderr)
|
||||||
|
|
||||||
def ask_new_profile_name(self):
|
def ask_new_profile_name(self):
|
||||||
return simpledialog.askstring("Add Profile", "Enter name:", parent=self.master)
|
return simpledialog.askstring("Add Profile", "Enter name:", parent=self.master)
|
||||||
|
|
||||||
@ -1422,6 +1506,8 @@ class MainFrame(ttk.Frame):
|
|||||||
self.checkout_branch_button,
|
self.checkout_branch_button,
|
||||||
self.refresh_history_button,
|
self.refresh_history_button,
|
||||||
self.history_branch_filter_combo,
|
self.history_branch_filter_combo,
|
||||||
|
self.commit_message_text,
|
||||||
|
self.autocommit_checkbox,
|
||||||
]
|
]
|
||||||
# log_handler.log_debug(f"Setting action widgets state to: {state}", func_name="set_action_widgets_state") # Usa log_handler
|
# log_handler.log_debug(f"Setting action widgets state to: {state}", func_name="set_action_widgets_state") # Usa log_handler
|
||||||
for widget in widgets:
|
for widget in widgets:
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user