diff --git a/countess/gui/config.py b/countess/gui/config.py index 71916f9..1fcef3a 100644 --- a/countess/gui/config.py +++ b/countess/gui/config.py @@ -22,8 +22,7 @@ TextParam, ) from ..core.plugins import BasePlugin -from .unicode import UNICODE_CHECK, UNICODE_CROSS, UNICODE_PLUS, UNICODE_UNCHECK - +from .widgets import add_button, delete_button, BooleanCheckbox def is_nan(v): return v is None or v is np.nan or (isinstance(v, float) and math.isnan(v)) @@ -74,7 +73,7 @@ def __init__( # pylint: disable=R0912,R0915 else: self.entry["state"] = "readonly" elif isinstance(parameter, BooleanParam): - self.entry = tk.Button(tk_parent, width=2, command=self.toggle_checkbox_callback) + self.entry = BooleanCheckbox(tk_parent, command=self.toggle_checkbox_callback) self.set_checkbox_value() elif isinstance(parameter, FileParam): self.entry = tk.Label(tk_parent, text=parameter.value) @@ -115,12 +114,7 @@ def __init__( # pylint: disable=R0912,R0915 tk.Label(label_frame_label, text=parameter.label).grid(row=0, column=0, padx=5) self.entry = tk.LabelFrame(tk_parent, labelwidget=label_frame_label, padx=10, pady=5) if not parameter.read_only: - self.button = tk.Button( - label_frame_label, - text=UNICODE_PLUS, - width=2, - command=self.add_row_callback, - ) + self.button = add_button(label_frame_label, command=self.add_row_callback) self.button.grid(row=0, column=1, padx=10) drc = self.delete_row_callback if not parameter.read_only else None @@ -145,7 +139,7 @@ def __init__( # pylint: disable=R0912,R0915 if isinstance(parameter, ArrayParam): drc = self.delete_row_callback if not parameter.read_only else None self.update_subwrappers(parameter.params, drc) - self.button = tk.Button(tk_parent, text=UNICODE_PLUS, width=2, command=self.add_row_callback) + self.button = add_button(tk_parent, command=self.add_row_callback) else: self.update_subwrappers(parameter.params.values(), None) else: @@ -156,12 +150,7 @@ def __init__( # pylint: disable=R0912,R0915 # XXX hang on, what if it's an array in an array? if delete_callback and not self.button: - self.button = tk.Button( - tk_parent, - text=UNICODE_CROSS, - width=2, - command=lambda: delete_callback(self), - ) + self.button = delete_button(tk_parent, command=lambda: delete_callback(self)) if not isinstance(parameter, BooleanParam): self.entry.grid(sticky=tk.EW, padx=10, pady=5) @@ -272,12 +261,7 @@ def update_subwrappers_tabular(self, params, delete_row_callback): ) self.subwrappers[pp].entry.grid(row=n + 1, column=m + 1, padx=10) if delete_row_callback: - button = tk.Button( - self.entry, - text=UNICODE_CROSS, - width=2, - command=partial(delete_row_callback, self, n), - ) + button = delete_button(self.entry, command=partial(delete_row_callback, self, n)) button.grid(row=n + 1, column=len(subparams) + 1, padx=10) self.subwrapper_buttons.append(button) @@ -306,12 +290,7 @@ def _command_drc(param, label_frame): level=self.level + 1, ) if delete_row_callback: - button = tk.Button( - label_frame_label, - text=UNICODE_CROSS, - width=2, - command=partial(_command_drc, p, label_frame), - ) + button = delete_button(label_frame_label, command=partial(_command_drc, p, label_frame)) button.grid(row=0, column=1, padx=10) self.subwrappers[p].entry.grid(row=n, column=0, padx=10) @@ -403,20 +382,11 @@ def widget_modified_callback(self, *_): def set_checkbox_value(self): if self.parameter.hide: - self.entry["text"] = "" - self.entry["fg"] = self.entry["bg"] - self.entry["state"] = tk.DISABLED - self.entry["bd"] = 0 + self.entry.set_value(None) elif self.parameter.value: - self.entry["text"] = UNICODE_CHECK - self.entry["fg"] = "black" - self.entry["state"] = tk.NORMAL - self.entry["bd"] = 1 + self.entry.set_value(True) else: - self.entry["text"] = UNICODE_UNCHECK - self.entry["fg"] = "grey" - self.entry["state"] = tk.NORMAL - self.entry["bd"] = 1 + self.entry.set_value(False) def toggle_checkbox_callback(self, *_): if self.parameter.read_only or self.parameter.hide: diff --git a/countess/gui/main.py b/countess/gui/main.py index 473a7b9..f1e02fa 100644 --- a/countess/gui/main.py +++ b/countess/gui/main.py @@ -18,7 +18,7 @@ from countess.gui.logger import LoggerFrame from countess.gui.tabular import TabularDataFrame from countess.gui.tree import FlippyCanvas, GraphWrapper -from countess.gui.unicode import UNICODE_INFO +from countess.gui.widgets import info_button from countess.utils.pandas import concat_dataframes # import faulthandler @@ -111,9 +111,7 @@ def show_config_subframe(self): descr, ) if self.node.plugin.link: - tk.Button(self.frame, text=UNICODE_INFO, fg="blue", command=self.on_info_button_press).place( - anchor=tk.NE, relx=1, y=50 - ) + info_button(self.frame, command=self.on_info_button_press).place(anchor=tk.NE, relx=1, y=50) # self.node.prepare(self.logger) # self.node.plugin.update() self.configurator = PluginConfigurator(self.config_canvas, self.node.plugin, self.config_change_callback) diff --git a/countess/gui/unicode.py b/countess/gui/unicode.py deleted file mode 100644 index 08bcdd6..0000000 --- a/countess/gui/unicode.py +++ /dev/null @@ -1,24 +0,0 @@ -import tkinter as tk - -# Tcl support for unicode starts in version 8.1, April 1999. - -def unicode_is_broken(): - root = tk.Tk() - font = tk_font.Font(root) - is_broken = font.measure("\u2795") > 3 * font.measure("m") - root.destroy() - return is_broken - -if tk.TclVersion < 8.1: - UNICODE_CHECK = "Y" - UNICODE_UNCHECK = "N" - UNICODE_CROSS = "X" - UNICODE_PLUS = "+" - UNICODE_INFO = "i" - -else: - UNICODE_CHECK = "\u2714" - UNICODE_UNCHECK = "\u2717" - UNICODE_CROSS = "\u2715" - UNICODE_PLUS = "\u2795" - UNICODE_INFO = "\u2139" diff --git a/countess/gui/widgets.py b/countess/gui/widgets.py new file mode 100644 index 0000000..d711bee --- /dev/null +++ b/countess/gui/widgets.py @@ -0,0 +1,49 @@ +from functools import cache + +import tkinter as tk +import tkinter.font as tk_font + + +@cache +def unicode_is_broken(): + font = tk_font.Font() + return font.measure("\u2795") > 3 * font.measure("m") + +def info_button(parent, *args, **kwargs): + kwargs["text"] = "i" if unicode_is_broken() else "\u2139" + kwargs["fg"] = "blue" + return tk.Button(parent, *args, **kwargs) + +def add_button(parent, *args, **kwargs): + kwargs["text"] = "+" if unicode_is_broken() else "\u2795" + kwargs["width"] = 2 + return tk.Button(parent, *args, **kwargs) + +def delete_button(parent, *args, **kwargs): + kwargs["text"] = "X" if unicode_is_broken() else "\u2715" + kwargs["width"] = 2 + return tk.Button(parent, *args, **kwargs) + + +class BooleanCheckbox(tk.Button): + + def __init__(self, *args, **kwargs): + kwargs["width"] = 2 + super().__init__(*args, **kwargs) + + def set_value(self, value): + if value is None: + self["text"] = "" + self["fg"] = self["bg"] + self["state"] = tk.DISABLED + self["bd"] = 0 + elif value: + self["text"] = "Y" if unicode_is_broken() else "\u2714" + self["fg"] = "black" + self["state"] = tk.NORMAL + self["bd"] = 1 + else: + self["text"] = "N" if unicode_is_broken() else "\u2717" + self["fg"] = "grey" + self["state"] = tk.NORMAL + self["bd"] = 1