Apr-01-2022, 08:02 PM
Not Temperature, Quantity, a generic dimension with an associate set of units. Temperature would be an instance of Quantity and it would have a base unit of Celsius. You could add units to Temperature like Fahrenheit, Kelvin, Rankine, Newton. Each of these units would know how to convert to and from Celsius. A converter program could take this information and automatically support converting between any of these units. From 1 Quantity and 4 additional units (5 total) you get 20 unit converters.
The converter panel would have three combo boxes. One for dimension, one for input units and one for output units. There would be an entry for the input value, and some sort of display for the output value. When you enter a value it is converted from input units to base units, and that value converted to output units and displayed in the window.
Like this:
The converter panel would have three combo boxes. One for dimension, one for input units and one for output units. There would be an entry for the input value, and some sort of display for the output value. When you enter a value it is converted from input units to base units, and that value converted to output units and displayed in the window.
Like this:
import tkinter as tk from tkinter import ttk font = ("", 20) class Unit: """Measurement unit for a Quantity""" def __init__(self, name, scale, offset=0): """ Scale and offset are values used in y = x * scale + offset where x is the value in base units and y the value in my units """ self.name = name self.scale = scale self.offset = offset def from_base(self, value): """Convert value from my units to base units""" return value * self.scale + self.offset def to_base(self, value): """Convert value from my units to base units""" return (value - self.offset) / self.scale class Quantity: """Things like Mass, Force, Distance, Time""" def __init__(self, name, base_unit, units): self.name = name self.base_unit = base_unit self.units = {unit.name:unit for unit in units} def convert(self, value, src, dst): """Convert value from src units to dst units""" if src != self.base_unit: value = self.units[src].to_base(value) if dst != self.base_unit: value = self.units[dst].from_base(value) return value class ComboBox(ttk.Combobox): """Combobox with built in variable and set_values method.""" def __init__(self, *args, **kwargs): self.variable = tk.StringVar() super().__init__(*args, textvariable=self.variable, **kwargs) def set_values(self, values): self["values"]=values width = max(map(len, values)) if width > self["width"]: self["width"] = width self.variable.set(values[0]) def value(self): return self.variable.get() class ConversionWindow(tk.Tk): """A program for doing unit conversion""" def __init__(self): super().__init__() self.quantities = {} self.title("Converter") # Make a combobox for selecting the quantity x = tk.Label(self, text="Convert Units") x.pack(side=tk.TOP, padx=5, pady=5) self.quantity_selector = ComboBox(self) self.quantity_selector.pack(side=tk.TOP, fill=tk.X, padx=5, pady=5) self.quantity_selector.variable.trace("w", self.quantity_changed) # Some frames for making a nice layout frame = tk.Frame(self) frame.pack(side=tk.TOP, fill=tk.BOTH) inp_frame = tk.Frame(self) inp_frame.pack(padx=5, pady=5, side=tk.LEFT, fill=tk.BOTH) out_frame = tk.Frame(self) out_frame.pack(padx=5, pady=5, side=tk.LEFT, fill=tk.BOTH) # Make an entry for typing in the input value and a combobox for selecing # the input units self.inp_value = tk.DoubleVar(self, 0.0) self.inp_value.trace("w", self.convert) tk.Entry(inp_frame, textvariable=self.inp_value, width=8, font=font, justify=tk.RIGHT) \ .pack(side=tk.TOP, fill=tk.X) self.inp_units = ComboBox(inp_frame) self.inp_units.pack(side=tk.TOP, fill=tk.X) self.inp_units.variable.trace("w", self.convert) # Make a label for displaying the output value and a combobox for selecting # the output units self.out_value = tk.StringVar() tk.Label(out_frame, textvariable=self.out_value, width=8, font=font, anchor="e", bg='white') \ .pack(side=tk.TOP, fill=tk.X) self.out_units = ComboBox(out_frame) self.out_units.pack(side=tk.TOP, fill=tk.X) self.out_units.variable.trace("w", self.convert) def add_quantity(self, quantity): """Add a quantity to the converter""" self.quantities[quantity.name] = quantity self.quantity_selector.set_values(list(self.quantities.keys())) return self def quantity_changed(self, *_): """Called when the quantity selector value changes""" self.quantity = self.quantities[self.quantity_selector.value()] units = [self.quantity.base_unit] + list(self.quantity.units.keys()) self.inp_units.set_values(units) self.out_units.set_values(units) self.inp_value.set(1.0) def convert(self, *_): """Called when input value or unit selection changes""" inp_units = self.inp_units.value() out_units = self.out_units.value() try: value = self.quantity.convert(self.inp_value.get(), inp_units, out_units) self.out_value.set(f"{value:.4f}") except (tk.TclError, KeyError): self.out_value.set("Input Error") def main(): app = ConversionWindow() app.add_quantity(Quantity("Temperature", "Celsius", [ Unit("Fahrenheit", 1.8, 32), Unit("Kelvin", 1, 273.15), Unit("Rankine", 1.8, 273.15/1.8), Unit("Newton", 0.33), Unit("Remer", 21/40, 7.5)])) app.add_quantity(Quantity("Distance", "Meter", [ Unit("Centimeter", 100), Unit("Millimeter", 1000), Unit("Kilometer", 1/1000), Unit("Inch", 39.3700787), Unit("Foot", 3.2808399), Unit("Yard", 1.0936133), Unit("Mile", 0.00062137)])) app.add_quantity(Quantity("Volume", "Liter", [ Unit("Millileter", 1000), Unit("Cubic Centimeter", 1000), Unit("Hogshead", 0.00419321), Unit("Gallon", 0.26417205), Unit("Quart", 1.05668821), Unit("Pint", 2.11337642), Unit("Cup", 4.22675284), Unit("Ounce", 33.8140227)])) app.mainloop() if __name__ == "__main__": main()Not positive about my math, but I think this supports 154 different unit conversions and it's not even 154 lines long! Excellent value for the keystroke.