"""
PDB Fixing configuration dialog for HBAT GUI.
This module provides a dedicated dialog for configuring PDB fixing parameters,
separate from the main geometry parameter settings.
"""
import tkinter as tk
from tkinter import ttk
from typing import Dict, Any, Optional
from ..constants.parameters import ParametersDefault
[docs]
class PDBFixingDialog:
"""Dialog for configuring PDB fixing parameters."""
[docs]
def __init__(self, parent: tk.Tk):
"""Initialize PDB fixing dialog.
:param parent: Parent window
:type parent: tk.Tk
"""
self.parent = parent
self.result = None
# Create dialog window
self.dialog = tk.Toplevel(parent)
self.dialog.title("PDB Fixing Settings")
self.dialog.geometry("800x600")
self.dialog.resizable(False, False)
# Make dialog modal
self.dialog.transient(parent)
self.dialog.grab_set()
# Initialize variables
self._init_variables()
# Create widgets
self._create_widgets()
# Center the dialog
self.dialog.update_idletasks()
x = (self.dialog.winfo_screenwidth() // 2) - (self.dialog.winfo_width() // 2)
y = (self.dialog.winfo_screenheight() // 2) - (self.dialog.winfo_height() // 2)
self.dialog.geometry(f"+{x}+{y}")
# Handle window closing
self.dialog.protocol("WM_DELETE_WINDOW", self._cancel)
def _init_variables(self):
"""Initialize tkinter variables with default values."""
self.fix_pdb_enabled = tk.BooleanVar(value=ParametersDefault.FIX_PDB_ENABLED)
self.fix_pdb_method = tk.StringVar(value=ParametersDefault.FIX_PDB_METHOD)
self.fix_pdb_add_hydrogens = tk.BooleanVar(value=ParametersDefault.FIX_PDB_ADD_HYDROGENS)
self.fix_pdb_add_heavy_atoms = tk.BooleanVar(value=ParametersDefault.FIX_PDB_ADD_HEAVY_ATOMS)
self.fix_pdb_replace_nonstandard = tk.BooleanVar(value=ParametersDefault.FIX_PDB_REPLACE_NONSTANDARD)
self.fix_pdb_remove_heterogens = tk.BooleanVar(value=ParametersDefault.FIX_PDB_REMOVE_HETEROGENS)
self.fix_pdb_keep_water = tk.BooleanVar(value=ParametersDefault.FIX_PDB_KEEP_WATER)
def _create_widgets(self):
"""Create dialog widgets."""
# Main frame with padding
main_frame = ttk.Frame(self.dialog, padding="20")
main_frame.pack(fill=tk.BOTH, expand=True)
# Title
title_label = ttk.Label(
main_frame,
text="PDB Structure Fixing Settings",
font=("TkDefaultFont", 12, "bold")
)
title_label.pack(pady=(0, 20))
# Enable PDB fixing checkbox
self.enable_check = ttk.Checkbutton(
main_frame,
text="Enable PDB structure fixing",
variable=self.fix_pdb_enabled,
command=self._on_enable_changed
)
self.enable_check.pack(anchor=tk.W, pady=(0, 15))
# Method selection frame
method_frame = ttk.LabelFrame(main_frame, text="Fixing Method", padding="10")
method_frame.pack(fill=tk.X, pady=(0, 15))
ttk.Radiobutton(
method_frame,
text="PDBFixer (recommended for proteins)",
variable=self.fix_pdb_method,
value="pdbfixer",
command=self._on_method_changed,
).pack(anchor=tk.W)
ttk.Radiobutton(
method_frame,
text="OpenBabel",
variable=self.fix_pdb_method,
value="openbabel",
command=self._on_method_changed,
).pack(anchor=tk.W)
# Operations frame
operations_frame = ttk.LabelFrame(main_frame, text="Operations", padding="10")
operations_frame.pack(fill=tk.BOTH, expand=True, pady=(0, 15))
# Add hydrogens (both methods)
self.add_hydrogens_check = ttk.Checkbutton(
operations_frame,
text="Add missing hydrogen atoms",
variable=self.fix_pdb_add_hydrogens
)
self.add_hydrogens_check.pack(anchor=tk.W, pady=2)
# PDBFixer-only operations
self.add_heavy_atoms_check = ttk.Checkbutton(
operations_frame,
text="Add missing heavy atoms (PDBFixer only)",
variable=self.fix_pdb_add_heavy_atoms
)
self.add_heavy_atoms_check.pack(anchor=tk.W, pady=2)
self.replace_nonstandard_check = ttk.Checkbutton(
operations_frame,
text="Replace nonstandard residues (PDBFixer only)",
variable=self.fix_pdb_replace_nonstandard
)
self.replace_nonstandard_check.pack(anchor=tk.W, pady=2)
self.remove_heterogens_check = ttk.Checkbutton(
operations_frame,
text="Remove heterogens (PDBFixer only)",
variable=self.fix_pdb_remove_heterogens,
command=self._on_remove_heterogens_changed
)
self.remove_heterogens_check.pack(anchor=tk.W, pady=2)
# Keep water sub-option
self.keep_water_check = ttk.Checkbutton(
operations_frame,
text=" Keep water molecules",
variable=self.fix_pdb_keep_water
)
self.keep_water_check.pack(anchor=tk.W, pady=2)
# Store PDBFixer-only widgets
self.pdbfixer_only_widgets = [
self.add_heavy_atoms_check,
self.replace_nonstandard_check,
self.remove_heterogens_check,
self.keep_water_check
]
# Button frame
button_frame = ttk.Frame(main_frame)
button_frame.pack(fill=tk.X, pady=(20, 0))
ttk.Button(
button_frame,
text="OK",
command=self._ok
).pack(side=tk.RIGHT, padx=(5, 0))
ttk.Button(
button_frame,
text="Cancel",
command=self._cancel
).pack(side=tk.RIGHT)
ttk.Button(
button_frame,
text="Reset to Defaults",
command=self._reset_defaults
).pack(side=tk.LEFT, padx=(0, 5))
ttk.Button(
button_frame,
text="Manage Presets...",
command=self._open_preset_manager
).pack(side=tk.LEFT, padx=5)
# Initialize widget states
self._update_widget_states()
def _on_enable_changed(self):
"""Handle enable checkbox change."""
self._update_widget_states()
def _on_method_changed(self):
"""Handle method selection change."""
self._update_widget_states()
def _on_remove_heterogens_changed(self):
"""Handle remove heterogens checkbox change."""
self._update_widget_states()
def _update_widget_states(self):
"""Update widget enable/disable states based on selections."""
enabled = self.fix_pdb_enabled.get()
method = self.fix_pdb_method.get()
# Enable/disable based on main checkbox
for widget in [self.add_hydrogens_check] + self.pdbfixer_only_widgets:
widget.configure(state="normal" if enabled else "disabled")
# Further disable PDBFixer-only options if OpenBabel selected
if enabled and method == "openbabel":
for widget in self.pdbfixer_only_widgets:
widget.configure(state="disabled")
# Enable keep water only if remove heterogens is checked
if enabled and method == "pdbfixer" and self.fix_pdb_remove_heterogens.get():
self.keep_water_check.configure(state="normal")
else:
self.keep_water_check.configure(state="disabled")
def _reset_defaults(self):
"""Reset all values to defaults."""
self.fix_pdb_enabled.set(ParametersDefault.FIX_PDB_ENABLED)
self.fix_pdb_method.set(ParametersDefault.FIX_PDB_METHOD)
self.fix_pdb_add_hydrogens.set(ParametersDefault.FIX_PDB_ADD_HYDROGENS)
self.fix_pdb_add_heavy_atoms.set(ParametersDefault.FIX_PDB_ADD_HEAVY_ATOMS)
self.fix_pdb_replace_nonstandard.set(ParametersDefault.FIX_PDB_REPLACE_NONSTANDARD)
self.fix_pdb_remove_heterogens.set(ParametersDefault.FIX_PDB_REMOVE_HETEROGENS)
self.fix_pdb_keep_water.set(ParametersDefault.FIX_PDB_KEEP_WATER)
self._update_widget_states()
def _ok(self):
"""Handle OK button - save settings and close."""
self.result = {
"enabled": self.fix_pdb_enabled.get(),
"method": self.fix_pdb_method.get(),
"add_hydrogens": self.fix_pdb_add_hydrogens.get(),
"add_heavy_atoms": self.fix_pdb_add_heavy_atoms.get(),
"replace_nonstandard": self.fix_pdb_replace_nonstandard.get(),
"remove_heterogens": self.fix_pdb_remove_heterogens.get(),
"keep_water": self.fix_pdb_keep_water.get()
}
self.dialog.destroy()
def _cancel(self):
"""Handle Cancel button - close without saving."""
self.result = None
self.dialog.destroy()
[docs]
def get_parameters(self) -> Optional[Dict[str, Any]]:
"""Get the configured parameters.
:returns: Dictionary of PDB fixing parameters or None if cancelled
:rtype: Optional[Dict[str, Any]]
"""
self.dialog.wait_window()
return self.result
[docs]
def set_parameters(self, params: Dict[str, Any]):
"""Set parameters from a dictionary.
:param params: Dictionary of PDB fixing parameters
:type params: Dict[str, Any]
"""
if "enabled" in params:
self.fix_pdb_enabled.set(params["enabled"])
if "method" in params:
self.fix_pdb_method.set(params["method"])
if "add_hydrogens" in params:
self.fix_pdb_add_hydrogens.set(params["add_hydrogens"])
if "add_heavy_atoms" in params:
self.fix_pdb_add_heavy_atoms.set(params["add_heavy_atoms"])
if "replace_nonstandard" in params:
self.fix_pdb_replace_nonstandard.set(params["replace_nonstandard"])
if "remove_heterogens" in params:
self.fix_pdb_remove_heterogens.set(params["remove_heterogens"])
if "keep_water" in params:
self.fix_pdb_keep_water.set(params["keep_water"])
self._update_widget_states()
def _open_preset_manager(self):
"""Open the preset manager dialog."""
from .preset_manager_dialog import PresetManagerDialog
from ..core.analysis import AnalysisParameters
from tkinter import messagebox
# Create a dummy AnalysisParameters object with current PDB fixing settings
# The preset manager expects AnalysisParameters, so we'll use default geometry params
# and the user can focus on PDB fixing presets
current_params = AnalysisParameters()
# Open preset manager
dialog = PresetManagerDialog(self.dialog, current_params)
result = dialog.get_result()
if result and "parameters" in result and "pdb_fixing" in result["parameters"]:
# Apply only the PDB fixing part of the loaded preset
pdb_params = result["parameters"]["pdb_fixing"]
self.set_parameters(pdb_params)
messagebox.showinfo("Success", "PDB fixing preset loaded successfully")
elif result:
messagebox.showwarning("Warning", "Selected preset does not contain PDB fixing parameters")