# -*- coding: utf-8 -*-
"""Labeled combobox widget with units.
The goal of these widgets is twofold: to make it easier for developers
to implement dialogs with compound widgets, and to naturally
standardize the user interface presented to the user.
"""
import logging
from seamm_util import Q_, units_class
import seamm_widgets as sw
import tkinter as tk
import tkinter.ttk as ttk
logger = logging.getLogger(__name__)
options = {
"unitcombobox": {
"as_quantity": "as_quantity",
},
"units": {
"class_": "class_",
"cursor": "cursor",
"exportselection": "exportselection",
"unitsheight": "height",
"unitsjustify": "justify",
"postcommand": "postcommand",
"style": "style",
"unitstakefocus": "takefocus",
"variable": "textvariable",
"unitsvalidate": "validate",
"unitsvalidatecommand": "validatecommand",
"unitswidth": "width",
"unitsxscrollcommand": "xscrollcommand",
},
}
[docs]class UnitCombobox(sw.LabeledCombobox):
def __init__(self, parent, *args, **kwargs):
"""Initialize the instance"""
class_ = kwargs.pop("class_", "MUnitCombobox")
super().__init__(parent, class_=class_)
interior = self.interior
# unitcombobox options
self.as_quantity = kwargs.pop("as_quantity", False)
# units combobox
unitsheight = kwargs.pop("unitsheight", 7)
unitswidth = kwargs.pop("unitswidth", 10)
unitsstate = kwargs.pop("unitsstate", "readonly")
self.units = ttk.Combobox(
interior, height=unitsheight, width=unitswidth, state=unitsstate
)
self.units.grid(row=0, column=0, sticky=tk.EW)
# interior frame
self.interior = ttk.Frame(interior)
self.interior.grid(row=0, column=1, sticky=tk.NSEW)
self.config(**kwargs)
@property
def value(self):
return self.get()
@value.setter
def value(self, value):
self.set(value)
[docs] def show(self, *args):
"""Show only the specified subwidgets.
'all' or no arguments reverts to showing all"""
super().show(*args)
show_all = len(args) == 0 or args[0] == "all"
if show_all or "units" in args:
self.units.grid(row=0, column=0, sticky=tk.W)
else:
self.units.grid_forget()
[docs] def set(self, value, unit_string=None):
"""Set the the value and units"""
if value is None:
return
# the value may have units or be a plain value
if isinstance(value, units_class):
self.combobox.set(value.magnitude)
dimensionality = value.dimensionality
current_units = self.units.cget("values")
if len(current_units) > 0:
for unit in current_units:
if unit != "":
if Q_(unit).dimensionality != dimensionality:
self.units.configure(values=[])
current_units = []
break
if len(current_units) == 0:
self.set_units([*sw.default_units[str(dimensionality)], ""])
self.units.set("{0.units:~}".format(value).replace(" ", ""))
elif unit_string is not None:
self.combobox.set(value)
dimensionality = Q_(unit_string).dimensionality
current_units = self.units.cget("values")
if len(current_units) > 0:
for unit in current_units:
if unit != "":
if Q_(unit).dimensionality != dimensionality:
self.units.configure(values=[])
current_units = []
break
if len(current_units) == 0:
self.set_units([*sw.default_units[str(dimensionality)], ""])
self.units.set(unit_string)
else:
self.combobox.set(value)
self.set_units("all")
self.units.set("")
[docs] def get(self):
"""return the current value with units"""
value = self.combobox.get()
if value in self.combobox.cget("values"):
return value
else:
unit = self.units.get()
if unit == "":
return value
elif self.as_quantity:
try:
magnitude = float(value)
return Q_(magnitude, unit)
except Exception:
return (value, unit)
else:
return (value, unit)
[docs] def set_units(self, values=None):
if values is None:
dimensionality = str(self.get().dimensionality)
self.units.config(values=sw.default_units[dimensionality])
elif values == "all":
tmp = [""]
for key in sw.default_units:
tmp += sw.default_units[key]
self.units.config(values=tmp)
else:
self.units.config(values=values)
[docs] def config(self, **kwargs):
"""Set the configuration of the megawidget"""
unitcombobox = options["unitcombobox"]
units = options["units"]
# cannot modify kwargs while iterating over it...
keys = [*kwargs.keys()]
for k in keys:
if k in unitcombobox and unitcombobox[k] in self.__dict__:
v = kwargs.pop(k)
self.__dict__[unitcombobox[k]] = v
elif k in units:
v = kwargs.pop(k)
self.units.config(**{units[k]: v})
# having removed our options, pass rest to parent
super().config(**kwargs)