pygui: updated validation to be wrapper classes around ttk.Entry for convenience and less code

This commit is contained in:
Blake Harnden 2020-05-11 22:00:52 -07:00
parent 150db07497
commit 22d813df63
8 changed files with 125 additions and 350 deletions

View file

@ -15,7 +15,6 @@ from core.gui.menubar import Menubar
from core.gui.nodeutils import NodeUtils from core.gui.nodeutils import NodeUtils
from core.gui.statusbar import StatusBar from core.gui.statusbar import StatusBar
from core.gui.toolbar import Toolbar from core.gui.toolbar import Toolbar
from core.gui.validation import InputValidation
WIDTH = 1000 WIDTH = 1000
HEIGHT = 800 HEIGHT = 800
@ -33,7 +32,6 @@ class Application(ttk.Frame):
self.right_frame = None self.right_frame = None
self.canvas = None self.canvas = None
self.statusbar = None self.statusbar = None
self.validation = None
self.progress = None self.progress = None
# fonts # fonts
@ -73,7 +71,6 @@ class Application(ttk.Frame):
self.master.protocol("WM_DELETE_WINDOW", self.on_closing) self.master.protocol("WM_DELETE_WINDOW", self.on_closing)
image = Images.get(ImageEnum.CORE, 16) image = Images.get(ImageEnum.CORE, 16)
self.master.tk.call("wm", "iconphoto", self.master._w, image) self.master.tk.call("wm", "iconphoto", self.master._w, image)
self.validation = InputValidation(self)
self.master.option_add("*tearOff", tk.FALSE) self.master.option_add("*tearOff", tk.FALSE)
def center(self): def center(self):

View file

@ -5,6 +5,7 @@ import tkinter as tk
from tkinter import font, ttk from tkinter import font, ttk
from typing import TYPE_CHECKING from typing import TYPE_CHECKING
from core.gui import validation
from core.gui.dialogs.dialog import Dialog from core.gui.dialogs.dialog import Dialog
from core.gui.themes import FRAME_PAD, PADX, PADY from core.gui.themes import FRAME_PAD, PADX, PADY
@ -21,7 +22,6 @@ class SizeAndScaleDialog(Dialog):
""" """
super().__init__(app, "Canvas Size and Scale") super().__init__(app, "Canvas Size and Scale")
self.canvas = self.app.canvas self.canvas = self.app.canvas
self.validation = app.validation
self.section_font = font.Font(weight="bold") self.section_font = font.Font(weight="bold")
width, height = self.canvas.current_dimensions width, height = self.canvas.current_dimensions
self.pixel_width = tk.IntVar(value=width) self.pixel_width = tk.IntVar(value=width)
@ -59,23 +59,11 @@ class SizeAndScaleDialog(Dialog):
frame.columnconfigure(3, weight=1) frame.columnconfigure(3, weight=1)
label = ttk.Label(frame, text="Width") label = ttk.Label(frame, text="Width")
label.grid(row=0, column=0, sticky="w", padx=PADX) label.grid(row=0, column=0, sticky="w", padx=PADX)
entry = ttk.Entry( entry = validation.PositiveIntEntry(frame, textvariable=self.pixel_width)
frame,
textvariable=self.pixel_width,
validate="key",
validatecommand=(self.validation.positive_int, "%P"),
)
entry.bind("<FocusOut>", lambda event: self.validation.focus_out(event, "0"))
entry.grid(row=0, column=1, sticky="ew", padx=PADX) entry.grid(row=0, column=1, sticky="ew", padx=PADX)
label = ttk.Label(frame, text="x Height") label = ttk.Label(frame, text="x Height")
label.grid(row=0, column=2, sticky="w", padx=PADX) label.grid(row=0, column=2, sticky="w", padx=PADX)
entry = ttk.Entry( entry = validation.PositiveIntEntry(frame, textvariable=self.pixel_height)
frame,
textvariable=self.pixel_height,
validate="key",
validatecommand=(self.validation.positive_int, "%P"),
)
entry.bind("<FocusOut>", lambda event: self.validation.focus_out(event, "0"))
entry.grid(row=0, column=3, sticky="ew", padx=PADX) entry.grid(row=0, column=3, sticky="ew", padx=PADX)
label = ttk.Label(frame, text="Pixels") label = ttk.Label(frame, text="Pixels")
label.grid(row=0, column=4, sticky="w") label.grid(row=0, column=4, sticky="w")
@ -87,23 +75,11 @@ class SizeAndScaleDialog(Dialog):
frame.columnconfigure(3, weight=1) frame.columnconfigure(3, weight=1)
label = ttk.Label(frame, text="Width") label = ttk.Label(frame, text="Width")
label.grid(row=0, column=0, sticky="w", padx=PADX) label.grid(row=0, column=0, sticky="w", padx=PADX)
entry = ttk.Entry( entry = validation.PositiveFloatEntry(frame, textvariable=self.meters_width)
frame,
textvariable=self.meters_width,
validate="key",
validatecommand=(self.validation.positive_float, "%P"),
)
entry.bind("<FocusOut>", lambda event: self.validation.focus_out(event, "0"))
entry.grid(row=0, column=1, sticky="ew", padx=PADX) entry.grid(row=0, column=1, sticky="ew", padx=PADX)
label = ttk.Label(frame, text="x Height") label = ttk.Label(frame, text="x Height")
label.grid(row=0, column=2, sticky="w", padx=PADX) label.grid(row=0, column=2, sticky="w", padx=PADX)
entry = ttk.Entry( entry = validation.PositiveFloatEntry(frame, textvariable=self.meters_height)
frame,
textvariable=self.meters_height,
validate="key",
validatecommand=(self.validation.positive_float, "%P"),
)
entry.bind("<FocusOut>", lambda event: self.validation.focus_out(event, "0"))
entry.grid(row=0, column=3, sticky="ew", padx=PADX) entry.grid(row=0, column=3, sticky="ew", padx=PADX)
label = ttk.Label(frame, text="Meters") label = ttk.Label(frame, text="Meters")
label.grid(row=0, column=4, sticky="w") label.grid(row=0, column=4, sticky="w")
@ -118,13 +94,7 @@ class SizeAndScaleDialog(Dialog):
frame.columnconfigure(1, weight=1) frame.columnconfigure(1, weight=1)
label = ttk.Label(frame, text=f"{PIXEL_SCALE} Pixels =") label = ttk.Label(frame, text=f"{PIXEL_SCALE} Pixels =")
label.grid(row=0, column=0, sticky="w", padx=PADX) label.grid(row=0, column=0, sticky="w", padx=PADX)
entry = ttk.Entry( entry = validation.PositiveFloatEntry(frame, textvariable=self.scale)
frame,
textvariable=self.scale,
validate="key",
validatecommand=(self.validation.positive_float, "%P"),
)
entry.bind("<FocusOut>", lambda event: self.validation.focus_out(event, "0"))
entry.grid(row=0, column=1, sticky="ew", padx=PADX) entry.grid(row=0, column=1, sticky="ew", padx=PADX)
label = ttk.Label(frame, text="Meters") label = ttk.Label(frame, text="Meters")
label.grid(row=0, column=2, sticky="w") label.grid(row=0, column=2, sticky="w")
@ -148,24 +118,12 @@ class SizeAndScaleDialog(Dialog):
label = ttk.Label(frame, text="X") label = ttk.Label(frame, text="X")
label.grid(row=0, column=0, sticky="w", padx=PADX) label.grid(row=0, column=0, sticky="w", padx=PADX)
entry = ttk.Entry( entry = validation.PositiveFloatEntry(frame, textvariable=self.x)
frame,
textvariable=self.x,
validate="key",
validatecommand=(self.validation.positive_float, "%P"),
)
entry.bind("<FocusOut>", lambda event: self.validation.focus_out(event, "0"))
entry.grid(row=0, column=1, sticky="ew", padx=PADX) entry.grid(row=0, column=1, sticky="ew", padx=PADX)
label = ttk.Label(frame, text="Y") label = ttk.Label(frame, text="Y")
label.grid(row=0, column=2, sticky="w", padx=PADX) label.grid(row=0, column=2, sticky="w", padx=PADX)
entry = ttk.Entry( entry = validation.PositiveFloatEntry(frame, textvariable=self.y)
frame,
textvariable=self.y,
validate="key",
validatecommand=(self.validation.positive_float, "%P"),
)
entry.bind("<FocusOut>", lambda event: self.validation.focus_out(event, "0"))
entry.grid(row=0, column=3, sticky="ew", padx=PADX) entry.grid(row=0, column=3, sticky="ew", padx=PADX)
label = ttk.Label(label_frame, text="Translates To") label = ttk.Label(label_frame, text="Translates To")
@ -179,35 +137,17 @@ class SizeAndScaleDialog(Dialog):
label = ttk.Label(frame, text="Lat") label = ttk.Label(frame, text="Lat")
label.grid(row=0, column=0, sticky="w", padx=PADX) label.grid(row=0, column=0, sticky="w", padx=PADX)
entry = ttk.Entry( entry = validation.FloatEntry(frame, textvariable=self.lat)
frame,
textvariable=self.lat,
validate="key",
validatecommand=(self.validation.float, "%P"),
)
entry.bind("<FocusOut>", lambda event: self.validation.focus_out(event, "0"))
entry.grid(row=0, column=1, sticky="ew", padx=PADX) entry.grid(row=0, column=1, sticky="ew", padx=PADX)
label = ttk.Label(frame, text="Lon") label = ttk.Label(frame, text="Lon")
label.grid(row=0, column=2, sticky="w", padx=PADX) label.grid(row=0, column=2, sticky="w", padx=PADX)
entry = ttk.Entry( entry = validation.FloatEntry(frame, textvariable=self.lon)
frame,
textvariable=self.lon,
validate="key",
validatecommand=(self.validation.float, "%P"),
)
entry.bind("<FocusOut>", lambda event: self.validation.focus_out(event, "0"))
entry.grid(row=0, column=3, sticky="ew", padx=PADX) entry.grid(row=0, column=3, sticky="ew", padx=PADX)
label = ttk.Label(frame, text="Alt") label = ttk.Label(frame, text="Alt")
label.grid(row=0, column=4, sticky="w", padx=PADX) label.grid(row=0, column=4, sticky="w", padx=PADX)
entry = ttk.Entry( entry = validation.FloatEntry(frame, textvariable=self.alt)
frame,
textvariable=self.alt,
validate="key",
validatecommand=(self.validation.float, "%P"),
)
entry.bind("<FocusOut>", lambda event: self.validation.focus_out(event, "0"))
entry.grid(row=0, column=5, sticky="ew") entry.grid(row=0, column=5, sticky="ew")
def draw_save_as_default(self): def draw_save_as_default(self):

View file

@ -5,6 +5,7 @@ import tkinter as tk
from tkinter import ttk from tkinter import ttk
from typing import TYPE_CHECKING from typing import TYPE_CHECKING
from core.gui import validation
from core.gui.dialogs.dialog import Dialog from core.gui.dialogs.dialog import Dialog
if TYPE_CHECKING: if TYPE_CHECKING:
@ -50,13 +51,7 @@ class ColorPickerDialog(Dialog):
frame.columnconfigure(3, weight=2) frame.columnconfigure(3, weight=2)
label = ttk.Label(frame, text="R: ") label = ttk.Label(frame, text="R: ")
label.grid(row=0, column=0) label.grid(row=0, column=0)
self.red_entry = ttk.Entry( self.red_entry = validation.RgbEntry(frame, width=4, textvariable=self.red)
frame,
width=4,
textvariable=self.red,
validate="key",
validatecommand=(self.app.validation.rgb, "%P"),
)
self.red_entry.grid(row=0, column=1, sticky="nsew") self.red_entry.grid(row=0, column=1, sticky="nsew")
scale = ttk.Scale( scale = ttk.Scale(
frame, frame,
@ -82,20 +77,13 @@ class ColorPickerDialog(Dialog):
frame.columnconfigure(3, weight=2) frame.columnconfigure(3, weight=2)
label = ttk.Label(frame, text="G: ") label = ttk.Label(frame, text="G: ")
label.grid(row=0, column=0) label.grid(row=0, column=0)
self.green_entry = ttk.Entry( self.green_entry = validation.RgbEntry(frame, width=4, textvariable=self.green)
frame,
width=4,
textvariable=self.green,
validate="key",
validatecommand=(self.app.validation.rgb, "%P"),
)
self.green_entry.grid(row=0, column=1, sticky="nsew") self.green_entry.grid(row=0, column=1, sticky="nsew")
scale = ttk.Scale( scale = ttk.Scale(
frame, frame,
from_=0, from_=0,
to=255, to=255,
value=0, value=0,
# length=200,
orient=tk.HORIZONTAL, orient=tk.HORIZONTAL,
variable=self.green_scale, variable=self.green_scale,
command=lambda x: self.scale_callback(self.green_scale, self.green), command=lambda x: self.scale_callback(self.green_scale, self.green),
@ -114,13 +102,7 @@ class ColorPickerDialog(Dialog):
frame.columnconfigure(3, weight=2) frame.columnconfigure(3, weight=2)
label = ttk.Label(frame, text="B: ") label = ttk.Label(frame, text="B: ")
label.grid(row=0, column=0) label.grid(row=0, column=0)
self.blue_entry = ttk.Entry( self.blue_entry = validation.RgbEntry(frame, width=4, textvariable=self.blue)
frame,
width=4,
textvariable=self.blue,
validate="key",
validatecommand=(self.app.validation.rgb, "%P"),
)
self.blue_entry.grid(row=0, column=1, sticky="nsew") self.blue_entry.grid(row=0, column=1, sticky="nsew")
scale = ttk.Scale( scale = ttk.Scale(
frame, frame,
@ -144,12 +126,7 @@ class ColorPickerDialog(Dialog):
frame.columnconfigure(0, weight=1) frame.columnconfigure(0, weight=1)
label = ttk.Label(frame, text="Selection: ") label = ttk.Label(frame, text="Selection: ")
label.grid(row=0, column=0, sticky="nsew") label.grid(row=0, column=0, sticky="nsew")
self.hex_entry = ttk.Entry( self.hex_entry = validation.HexEntry(frame, textvariable=self.hex)
frame,
textvariable=self.hex,
validate="key",
validatecommand=(self.app.validation.hex, "%P"),
)
self.hex_entry.grid(row=1, column=0, sticky="nsew") self.hex_entry.grid(row=1, column=0, sticky="nsew")
self.display = tk.Frame(frame, background=self.color, width=100, height=100) self.display = tk.Frame(frame, background=self.color, width=100, height=100)
self.display.grid(row=2, column=0) self.display.grid(row=2, column=0)

View file

@ -6,6 +6,7 @@ from tkinter import ttk
from typing import TYPE_CHECKING, Union from typing import TYPE_CHECKING, Union
from core.api.grpc import core_pb2 from core.api.grpc import core_pb2
from core.gui import validation
from core.gui.dialogs.colorpicker import ColorPickerDialog from core.gui.dialogs.colorpicker import ColorPickerDialog
from core.gui.dialogs.dialog import Dialog from core.gui.dialogs.dialog import Dialog
from core.gui.themes import PADX, PADY from core.gui.themes import PADX, PADY
@ -120,95 +121,65 @@ class LinkConfigurationDialog(Dialog):
label = ttk.Label(frame, text="Bandwidth (bps)") label = ttk.Label(frame, text="Bandwidth (bps)")
label.grid(row=row, column=0, sticky="ew") label.grid(row=row, column=0, sticky="ew")
entry = ttk.Entry( entry = validation.PositiveIntEntry(
frame, frame, empty_enabled=False, textvariable=self.bandwidth
textvariable=self.bandwidth,
validate="key",
validatecommand=(self.app.validation.positive_int, "%P"),
) )
entry.grid(row=row, column=1, sticky="ew", pady=PADY) entry.grid(row=row, column=1, sticky="ew", pady=PADY)
if not self.is_symmetric: if not self.is_symmetric:
entry = ttk.Entry( entry = validation.PositiveIntEntry(
frame, frame, empty_enabled=False, textvariable=self.down_bandwidth
textvariable=self.down_bandwidth,
validate="key",
validatecommand=(self.app.validation.positive_int, "%P"),
) )
entry.grid(row=row, column=2, sticky="ew", pady=PADY) entry.grid(row=row, column=2, sticky="ew", pady=PADY)
row = row + 1 row = row + 1
label = ttk.Label(frame, text="Delay (us)") label = ttk.Label(frame, text="Delay (us)")
label.grid(row=row, column=0, sticky="ew") label.grid(row=row, column=0, sticky="ew")
entry = ttk.Entry( entry = validation.PositiveIntEntry(
frame, frame, empty_enabled=False, textvariable=self.delay
textvariable=self.delay,
validate="key",
validatecommand=(self.app.validation.positive_int, "%P"),
) )
entry.grid(row=row, column=1, sticky="ew", pady=PADY) entry.grid(row=row, column=1, sticky="ew", pady=PADY)
if not self.is_symmetric: if not self.is_symmetric:
entry = ttk.Entry( entry = validation.PositiveIntEntry(
frame, frame, empty_enabled=False, textvariable=self.down_delay
textvariable=self.down_delay,
validate="key",
validatecommand=(self.app.validation.positive_int, "%P"),
) )
entry.grid(row=row, column=2, sticky="ew", pady=PADY) entry.grid(row=row, column=2, sticky="ew", pady=PADY)
row = row + 1 row = row + 1
label = ttk.Label(frame, text="Jitter (us)") label = ttk.Label(frame, text="Jitter (us)")
label.grid(row=row, column=0, sticky="ew") label.grid(row=row, column=0, sticky="ew")
entry = ttk.Entry( entry = validation.PositiveIntEntry(
frame, frame, empty_enabled=False, textvariable=self.jitter
textvariable=self.jitter,
validate="key",
validatecommand=(self.app.validation.positive_int, "%P"),
) )
entry.grid(row=row, column=1, sticky="ew", pady=PADY) entry.grid(row=row, column=1, sticky="ew", pady=PADY)
if not self.is_symmetric: if not self.is_symmetric:
entry = ttk.Entry( entry = validation.PositiveIntEntry(
frame, frame, empty_enabled=False, textvariable=self.down_jitter
textvariable=self.down_jitter,
validate="key",
validatecommand=(self.app.validation.positive_int, "%P"),
) )
entry.grid(row=row, column=2, sticky="ew", pady=PADY) entry.grid(row=row, column=2, sticky="ew", pady=PADY)
row = row + 1 row = row + 1
label = ttk.Label(frame, text="Loss (%)") label = ttk.Label(frame, text="Loss (%)")
label.grid(row=row, column=0, sticky="ew") label.grid(row=row, column=0, sticky="ew")
entry = ttk.Entry( entry = validation.PositiveFloatEntry(
frame, frame, empty_enabled=False, textvariable=self.loss
textvariable=self.loss,
validate="key",
validatecommand=(self.app.validation.positive_float, "%P"),
) )
entry.grid(row=row, column=1, sticky="ew", pady=PADY) entry.grid(row=row, column=1, sticky="ew", pady=PADY)
if not self.is_symmetric: if not self.is_symmetric:
entry = ttk.Entry( entry = validation.PositiveFloatEntry(
frame, frame, empty_enabled=False, textvariable=self.down_loss
textvariable=self.down_loss,
validate="key",
validatecommand=(self.app.validation.positive_float, "%P"),
) )
entry.grid(row=row, column=2, sticky="ew", pady=PADY) entry.grid(row=row, column=2, sticky="ew", pady=PADY)
row = row + 1 row = row + 1
label = ttk.Label(frame, text="Duplicate (%)") label = ttk.Label(frame, text="Duplicate (%)")
label.grid(row=row, column=0, sticky="ew") label.grid(row=row, column=0, sticky="ew")
entry = ttk.Entry( entry = validation.PositiveIntEntry(
frame, frame, empty_enabled=False, textvariable=self.duplicate
textvariable=self.duplicate,
validate="key",
validatecommand=(self.app.validation.positive_int, "%P"),
) )
entry.grid(row=row, column=1, sticky="ew", pady=PADY) entry.grid(row=row, column=1, sticky="ew", pady=PADY)
if not self.is_symmetric: if not self.is_symmetric:
entry = ttk.Entry( entry = validation.PositiveIntEntry(
frame, frame, empty_enabled=False, textvariable=self.down_duplicate
textvariable=self.down_duplicate,
validate="key",
validatecommand=(self.app.validation.positive_int, "%P"),
) )
entry.grid(row=row, column=2, sticky="ew", pady=PADY) entry.grid(row=row, column=2, sticky="ew", pady=PADY)
row = row + 1 row = row + 1
@ -229,11 +200,8 @@ class LinkConfigurationDialog(Dialog):
label = ttk.Label(frame, text="Width") label = ttk.Label(frame, text="Width")
label.grid(row=row, column=0, sticky="ew") label.grid(row=row, column=0, sticky="ew")
entry = ttk.Entry( entry = validation.PositiveFloatEntry(
frame, frame, empty_enabled=False, textvariable=self.width
textvariable=self.width,
validate="key",
validatecommand=(self.app.validation.positive_float, "%P"),
) )
entry.grid(row=row, column=1, sticky="ew", pady=PADY) entry.grid(row=row, column=1, sticky="ew", pady=PADY)

View file

@ -6,7 +6,7 @@ from typing import TYPE_CHECKING
import netaddr import netaddr
from core.gui import nodeutils from core.gui import nodeutils, validation
from core.gui.appconfig import ICONS_PATH from core.gui.appconfig import ICONS_PATH
from core.gui.dialogs.dialog import Dialog from core.gui.dialogs.dialog import Dialog
from core.gui.dialogs.emaneconfig import EmaneModelDialog from core.gui.dialogs.emaneconfig import EmaneModelDialog
@ -143,16 +143,7 @@ class NodeConfigDialog(Dialog):
# name field # name field
label = ttk.Label(frame, text="Name") label = ttk.Label(frame, text="Name")
label.grid(row=row, column=0, sticky="ew", padx=PADX, pady=PADY) label.grid(row=row, column=0, sticky="ew", padx=PADX, pady=PADY)
entry = ttk.Entry( entry = validation.NodeNameEntry(frame, textvariable=self.name, state=state)
frame,
textvariable=self.name,
validate="key",
validatecommand=(self.app.validation.name, "%P"),
state=state,
)
entry.bind(
"<FocusOut>", lambda event: self.app.validation.focus_out(event, "noname")
)
entry.grid(row=row, column=1, sticky="ew") entry.grid(row=row, column=1, sticky="ew")
row += 1 row += 1

View file

@ -4,7 +4,7 @@ import tkinter as tk
from tkinter import ttk from tkinter import ttk
from typing import TYPE_CHECKING from typing import TYPE_CHECKING
from core.gui import appconfig from core.gui import appconfig, validation
from core.gui.dialogs.dialog import Dialog from core.gui.dialogs.dialog import Dialog
from core.gui.themes import FRAME_PAD, PADX, PADY, scale_fonts from core.gui.themes import FRAME_PAD, PADX, PADY, scale_fonts
from core.gui.validation import LARGEST_SCALE, SMALLEST_SCALE from core.gui.validation import LARGEST_SCALE, SMALLEST_SCALE
@ -80,12 +80,8 @@ class PreferencesDialog(Dialog):
variable=self.gui_scale, variable=self.gui_scale,
) )
scale.grid(row=0, column=0, sticky="ew") scale.grid(row=0, column=0, sticky="ew")
entry = ttk.Entry( entry = validation.AppScaleEntry(
scale_frame, scale_frame, textvariable=self.gui_scale, width=4
textvariable=self.gui_scale,
width=4,
validate="key",
validatecommand=(self.app.validation.app_scale, "%P"),
) )
entry.grid(row=0, column=1) entry.grid(row=0, column=1)

View file

@ -3,71 +3,63 @@ input validation
""" """
import re import re
import tkinter as tk import tkinter as tk
from typing import TYPE_CHECKING from tkinter import ttk
import netaddr
from netaddr import IPNetwork
if TYPE_CHECKING:
from core.gui.app import Application
SMALLEST_SCALE = 0.5 SMALLEST_SCALE = 0.5
LARGEST_SCALE = 5.0 LARGEST_SCALE = 5.0
HEX_REGEX = re.compile("^([#]([0-9]|[a-f])+)$|^[#]$")
class InputValidation: class ValidationEntry(ttk.Entry):
def __init__(self, app: "Application"): empty = None
self.master = app.master
self.positive_int = None
self.positive_float = None
self.float = None
self.app_scale = None
self.name = None
self.ip4 = None
self.rgb = None
self.hex = None
self.register()
def register(self): def __init__(self, master=None, widget=None, empty_enabled=True, **kwargs) -> None:
self.positive_int = self.master.register(self.check_positive_int) super().__init__(master, widget, **kwargs)
self.positive_float = self.master.register(self.check_positive_float) cmd = self.register(self.is_valid)
self.float = self.master.register(self.check_float) self.configure(validate="key", validatecommand=(cmd, "%P"))
self.app_scale = self.master.register(self.check_scale_value) if self.empty is not None and empty_enabled:
self.name = self.master.register(self.check_node_name) self.bind("<FocusOut>", self.focus_out)
self.ip4 = self.master.register(self.check_ip4)
self.rgb = self.master.register(self.check_rbg)
self.hex = self.master.register(self.check_hex)
@classmethod def is_valid(self, s: str) -> bool:
def ip_focus_out(cls, event: tk.Event): raise NotImplementedError
value = event.widget.get()
try:
IPNetwork(value)
except netaddr.core.AddrFormatError:
event.widget.delete(0, tk.END)
event.widget.insert(tk.END, "invalid")
@classmethod def focus_out(self, _event: tk.Event) -> None:
def focus_out(cls, event: tk.Event, default: str): value = self.get()
value = event.widget.get() if not value:
if value == "": self.insert(tk.END, self.empty)
event.widget.insert(tk.END, default)
@classmethod
def check_positive_int(cls, s: str) -> bool: class PositiveIntEntry(ValidationEntry):
if len(s) == 0: empty = "0"
def is_valid(self, s: str) -> bool:
if not s:
return True return True
try: try:
int_value = int(s) value = int(s)
if int_value >= 0: return value >= 0
return True
return False
except ValueError: except ValueError:
return False return False
@classmethod
def check_float(cls, s: str) -> bool: class PositiveFloatEntry(ValidationEntry):
if len(s) == 0: empty = "0.0"
def is_valid(self, s: str) -> bool:
if not s:
return True
try:
value = float(s)
return value >= 0.0
except ValueError:
return False
class FloatEntry(ValidationEntry):
empty = "0.0"
def is_valid(self, s: str) -> bool:
if not s:
return True return True
try: try:
float(s) float(s)
@ -75,109 +67,50 @@ class InputValidation:
except ValueError: except ValueError:
return False return False
@classmethod
def check_positive_float(cls, s: str) -> bool:
if len(s) == 0:
return True
try:
float_value = float(s)
if float_value >= 0.0:
return True
return False
except ValueError:
return False
@classmethod class RgbEntry(ValidationEntry):
def check_node_name(cls, s: str) -> bool: def is_valid(self, s: str) -> bool:
if len(s) < 0:
return False
if len(s) == 0:
return True
for char in s:
if not char.isalnum() and char != "_":
return False
return True
@classmethod
def check_canvas_int(cls, s: str) -> bool:
if len(s) == 0:
return True
try:
int_value = int(s)
if int_value >= 0:
return True
return False
except ValueError:
return False
@classmethod
def check_canvas_float(cls, s: str) -> bool:
if not s:
return True
try:
float_value = float(s)
if float_value >= 0.0:
return True
return False
except ValueError:
return False
@classmethod
def check_scale_value(cls, s: str) -> bool:
if not s:
return True
try:
float_value = float(s)
if SMALLEST_SCALE <= float_value <= LARGEST_SCALE or float_value == 0:
return True
return False
except ValueError:
return False
@classmethod
def check_ip4(cls, s: str) -> bool:
if not s:
return True
pat = re.compile("^([0-9]+[.])*[0-9]*$")
if pat.match(s) is not None:
_32bits = s.split(".")
if len(_32bits) > 4:
return False
for _8bits in _32bits:
if (
(_8bits and int(_8bits) > 255)
or len(_8bits) > 3
or (_8bits.startswith("0") and len(_8bits) > 1)
):
return False
return True
else:
return False
@classmethod
def check_rbg(cls, s: str) -> bool:
if not s: if not s:
return True return True
if s.startswith("0") and len(s) >= 2: if s.startswith("0") and len(s) >= 2:
return False return False
try: try:
value = int(s) value = int(s)
if 0 <= value <= 255: return 0 <= value <= 255
return True
else:
return False
except ValueError: except ValueError:
return False return False
@classmethod
def check_hex(cls, s: str) -> bool: class HexEntry(ValidationEntry):
def is_valid(self, s: str) -> bool:
if not s: if not s:
return True return True
pat = re.compile("^([#]([0-9]|[a-f])+)$|^[#]$") if HEX_REGEX.match(s):
if pat.match(s): return 0 <= len(s) <= 7
if 0 <= len(s) <= 7:
return True
else:
return False
else: else:
return False return False
class NodeNameEntry(ValidationEntry):
empty = "noname"
def is_valid(self, s: str) -> bool:
if len(s) < 0:
return False
if len(s) == 0:
return True
for x in s:
if not x.isalnum() and x != "_":
return False
return True
class AppScaleEntry(ValidationEntry):
def is_valid(self, s: str) -> bool:
if not s:
return True
try:
float_value = float(s)
return SMALLEST_SCALE <= float_value <= LARGEST_SCALE or float_value == 0
except ValueError:
return False

View file

@ -6,7 +6,7 @@ from tkinter import filedialog, font, ttk
from typing import TYPE_CHECKING, Dict from typing import TYPE_CHECKING, Dict
from core.api.grpc import common_pb2, core_pb2 from core.api.grpc import common_pb2, core_pb2
from core.gui import themes from core.gui import themes, validation
from core.gui.themes import FRAME_PAD, PADX, PADY from core.gui.themes import FRAME_PAD, PADX, PADY
if TYPE_CHECKING: if TYPE_CHECKING:
@ -127,43 +127,16 @@ class ConfigFrame(ttk.Notebook):
button = ttk.Button(file_frame, text="...", command=func) button = ttk.Button(file_frame, text="...", command=func)
button.grid(row=0, column=1) button.grid(row=0, column=1)
else: else:
if "controlnet" in option.name and "script" not in option.name: entry = ttk.Entry(tab.frame, textvariable=value)
entry = ttk.Entry( entry.grid(row=index, column=1, sticky="ew")
tab.frame,
textvariable=value,
validate="key",
validatecommand=(self.app.validation.ip4, "%P"),
)
entry.grid(row=index, column=1, sticky="ew")
else:
entry = ttk.Entry(tab.frame, textvariable=value)
entry.grid(row=index, column=1, sticky="ew")
elif option.type in INT_TYPES: elif option.type in INT_TYPES:
value.set(option.value) value.set(option.value)
entry = ttk.Entry( entry = validation.PositiveIntEntry(tab.frame, textvariable=value)
tab.frame,
textvariable=value,
validate="key",
validatecommand=(self.app.validation.positive_int, "%P"),
)
entry.bind(
"<FocusOut>",
lambda event: self.app.validation.focus_out(event, "0"),
)
entry.grid(row=index, column=1, sticky="ew") entry.grid(row=index, column=1, sticky="ew")
elif option.type == core_pb2.ConfigOptionType.FLOAT: elif option.type == core_pb2.ConfigOptionType.FLOAT:
value.set(option.value) value.set(option.value)
entry = ttk.Entry( entry = validation.PositiveFloatEntry(tab.frame, textvariable=value)
tab.frame,
textvariable=value,
validate="key",
validatecommand=(self.app.validation.positive_float, "%P"),
)
entry.bind(
"<FocusOut>",
lambda event: self.app.validation.focus_out(event, "0"),
)
entry.grid(row=index, column=1, sticky="ew") entry.grid(row=index, column=1, sticky="ew")
else: else:
logging.error("unhandled config option type: %s", option.type) logging.error("unhandled config option type: %s", option.type)