Python Forum
Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Tkinter text overlap
#4
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:
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.
Reply


Messages In This Thread
Tkinter text overlap - by Frankduc - Mar-29-2022, 01:50 PM
RE: Tkinter text overlap - by deanhystad - Mar-31-2022, 03:58 PM
RE: Tkinter text overlap - by ndc85430 - Mar-31-2022, 04:24 PM
RE: Tkinter text overlap - by deanhystad - Apr-01-2022, 08:02 PM

Possibly Related Threads…
Thread Author Replies Views Last Post
  make all text in Right to left alignment in tkinter widgets jalal0034 1 1,457 Sep-27-2022, 06:42 PM
Last Post: Larz60+
  Exercise List Overlap Damian 7 3,358 Apr-02-2021, 07:39 AM
Last Post: Damian
  Rotated Rectangle overlap using Shapely pyNew 0 1,769 Feb-25-2021, 04:54 AM
Last Post: pyNew
  how to create a tool with tkinter to convert img to text rachidel07 3 2,677 Feb-05-2021, 12:21 PM
Last Post: deanhystad
  How to order the Cronjobs to avoid overlap or conflict sadhaonnisa 1 1,891 Oct-10-2020, 10:26 AM
Last Post: DeaD_EyE
  Can OpenGL object be overlap? hsunteik 4 5,204 Jan-19-2017, 02:43 PM
Last Post: Windspar

Forum Jump:

User Panel Messages

Announcements
Announcement #1 8/1/2020
Announcement #2 8/2/2020
Announcement #3 8/6/2020