Source code for ccu.fancyplots._gui.formatting
"""GUI elements for defining plot formatting parameters.
This module defines the :class:`FormattingSection` class.
"""
import tkinter as tk
from tkinter import ttk
from typing import TYPE_CHECKING
from typing import get_args
from typing import get_type_hints
from ccu.fancyplots._gui.frames import FancyFormatFrame
from ccu.fancyplots._gui.frames import UpdatableFrame
from ccu.fancyplots.data import DEFAULT_PARAMETERS
from ccu.fancyplots.data import FormattingParameters
from ccu.fancyplots.validation import type_hint_to_validator
if TYPE_CHECKING:
from ccu.fancyplots._gui.root import FancyPlotsGUI
[docs]
class FormattingSection(ttk.LabelFrame, UpdatableFrame):
"""A :class:`ttk.LabelFrame` containing all formatting parameters.
Note that instances of this class are listeners for the custom
<Validate> event emitted by :class:`.frames.FancyFormatFrame`
instances.
Attributes:
parent: The :class:`.root.FancyPlotsGUI`.
formatting_parameters: A :class:`.data.FormattingParameters` instance
mapping the names of formatting parameters to their values.
Defaults to a copy of :attr:`data.DEFAULT_PARAMETERS`.
frames: A list of :class:`FancyFormatFrame` instances in which the
formatting parameters are set.
"""
def __init__(
self,
parent: "FancyPlotsGUI",
*args,
parameters: FormattingParameters | None = None,
**kwargs,
) -> None:
"""Create a section for specifying plot formatting parameters.
Args:
parent: The running :class:`.root.FancyPlotsGUI` instance.
*args: Positional arguments for :class:`tkinter.Toplevel`.
parameters: A :class:`.formatting.FormattingParameters`
dictionary mapping parameter names to their values. Defauls
to :data:`.DEFAULT_PARAMETERS`.
**kwargs: Keyword arguments fo :class:`tkinter.Toplevel`.
"""
super().__init__(
parent._frame, *args, text="Plot Formatting", **kwargs
)
self.parent = parent
if parameters is None:
self.formatting_parameters = DEFAULT_PARAMETERS.copy()
else:
self.formatting_parameters = parameters
self.frames = self.initialize_frames()
self._organize()
self.bind("<Return>", self.parent._update_graph)
self.bind("<<Validate>>", self.update_parameters)
[docs]
def reset_defaults(self) -> None:
"""Reset the parameter values to their defaults."""
for frame in self.frames:
frame.value = DEFAULT_PARAMETERS[frame.label_text]
[docs]
def initialize_frames(self) -> list[FancyFormatFrame]:
"""Create ``ttk.LabelFrame`` widgets for setting formatting parameters.
Returns:
A list of :class:`FancyFormatFrame` widgets used to set formatting
parameters.
Raises:
NotImplementedError: Unsupported annotation type for formatting
parameter. Only annotated type hints are supported.
"""
frames = []
type_hints = get_type_hints(FormattingParameters, include_extras=True)
for label, value in self.formatting_parameters.items():
annotated_type_hint = type_hints[label]
type_hint, *tooltips = get_args(annotated_type_hint)
tooltip = " ".join(t for t in tooltips if isinstance(t, str))
validator = type_hint_to_validator(type_hint, label)
frame = FancyFormatFrame(
parent=self,
label=label,
value=value,
tooltip=tooltip,
validator=validator,
)
self.event_add("<<Validate>>", "None")
self.bind("<<Validate>>", self.update_parameters)
frames.append(frame)
return frames
[docs]
def _organize(self) -> None:
cols = 3
for i, frame in enumerate(self.frames):
col = i % cols
row = i // cols
frame.grid(column=col, row=row, sticky=tk.E + tk.W)
[docs]
def update_frames(self) -> None:
"""Update the values in the frame with the formatting parameters."""
for frame in self.frames:
param = frame.label_text
value = self.formatting_parameters[param]
frame.value = value
[docs]
def update_parameters(self, _: tk.Event) -> None:
"""Update :attr:`FormattingSection.formatting_parameters`."""
for frame in self.frames:
self.formatting_parameters[frame.label_text] = frame.python_value