2
\$\begingroup\$

After discovering Float Toy, my goal was to create my own version using Python and Tkinter. The results of the endeavor is shown below. Most of the functionality is complete, but the Equation widget does not automatically update itself yet. Does anyone have any simple suggestions for updating its labels?

#! /usr/bin/env python3
# http://evanw.github.io/float-toy/
import struct
import tkinter.ttk
from tkinter.constants import *


class FloatToy(tkinter.ttk.Frame):

    @classmethod
    def main(cls):
        tkinter.NoDefaultRoot()
        root = tkinter.Tk()
        cls.create_styles(root)
        root.title('Float Toy')
        root.resizable(False, False)
        toy = cls(root)
        toy.grid()
        root.mainloop()

    @staticmethod
    def create_styles(root):
        style = tkinter.ttk.Style(root)
        style.configure('S.TButton', background='#88F')
        style.configure('E.TButton', background='#8F8')
        style.configure('F.TButton', background='#F88')
        style.configure('S.TLabel', background='#CCF')
        style.configure('E.TLabel', background='#CFC')
        style.configure('F.TLabel', background='#FCC')

    def __init__(self, master=None, **kw):
        super().__init__(master, **kw)
        self.instructions = tkinter.ttk.Label(self)
        self.example_32 = Example(32, self)
        self.example_64 = Example(64, self)
        self.configure_all()
        self.grid_all()

    def configure_all(self):
        self.instructions['text'] = 'Click on a cell below to toggle bit ' \
                                    'values. Use this to build intuition ' \
                                    'for the IEEE float-point format.'

    def grid_all(self):
        self.instructions.grid(padx=5, pady=5, sticky=W)
        self.example_32.grid(padx=5, pady=5, sticky=W)
        self.example_64.grid(padx=5, pady=5, sticky=W)


class Example(tkinter.ttk.Labelframe):

    def __init__(self, width, master=None, **kw):
        kw['text'] = f'{width}-bit (float)'
        super().__init__(master, **kw)
        self.float = tkinter.DoubleVar(self)
        self.bits = Bits(width, self.float, self)
        self.equals = tkinter.ttk.Label(self)
        self.equation = Equation(width, self.float, self)
        self.configure_all()
        self.grid_all()

    def configure_all(self):
        self.float.trace_add('write', self.update_equals)
        self.float.set(1.0)

    def update_equals(self, *_):
        self.equals.configure(text=f' = {self.float.get()}')

    def grid_all(self):
        self.bits.grid(row=0, column=0, sticky=W)
        self.equals.grid(row=0, column=1, sticky=W)
        self.equation.grid(row=1, column=0, columnspan=2, sticky=W)


class Bits(tkinter.ttk.Frame):

    STYLE_RULES = {
        32: {
            range(0, 1): 'S',
            range(1, 9): 'E',
            range(9, 33): 'F'
        },
        64: {
            range(0, 1): 'S',
            range(1, 12): 'E',
            range(12, 65): 'F'
        }
    }

    STRUCT_RULES = {
        31: lambda bits: struct.unpack('>f', struct.pack('>L', bits))[0],
        63: lambda bits: struct.unpack('>d', struct.pack('>Q', bits))[0]
    }

    def __init__(self, width, var, master=None, **kw):
        super().__init__(master, **kw)
        self.buttons = [tkinter.ttk.Button(self) for _ in range(width)]
        self.var = var
        self.configure_all()
        self.grid_all()

    def configure_all(self):
        for column, button in enumerate(self.buttons):
            button.configure(text='0', width=1, style=self.get_style(column),
                             command=self.get_command(column))

    def get_style(self, column):
        for key, value in self.STYLE_RULES[len(self.buttons)].items():
            if column in key:
                return f'{value}.TButton'
        raise KeyError(f'column {column} has no style')

    def get_command(self, column):
        return lambda: self.toggle_bit(column)

    def toggle_bit(self, column):
        button = self.buttons[column]
        button['text'] = int(not int(button['text']))
        self.update_var()

    def update_var(self):
        bits = index = 0
        for index, button in enumerate(reversed(self.buttons)):
            bits |= int(button['text']) << index
        self.var.set(self.STRUCT_RULES[index](bits))

    def grid_all(self):
        for column, button in enumerate(self.buttons):
            button.grid(row=0, column=column)


class Equation(tkinter.ttk.Frame):

    def __init__(self, width, var, master=None, **kw):
        super().__init__(master, **kw)
        self.width = width
        self.var = var
        self.sign = tkinter.ttk.Label(self)
        self.times_1 = tkinter.ttk.Label(self)
        self.exponent = tkinter.ttk.Label(self)
        self.times_2 = tkinter.ttk.Label(self)
        self.fraction = tkinter.ttk.Label(self)
        self.configure_all()
        self.grid_all()

    def configure_all(self):
        self.sign.configure(text='1', style='S.TLabel')
        self.times_1.configure(text='×')
        self.exponent.configure(text='2 ** 0', style='E.TLabel')
        self.times_2.configure(text='×')
        self.fraction.configure(text='1', style='F.TLabel')

    def grid_all(self):
        self.sign.grid(row=0, column=0, padx=5, pady=5)
        self.times_1.grid(row=0, column=1, padx=5, pady=5)
        self.exponent.grid(row=0, column=2, padx=5, pady=5)
        self.times_2.grid(row=0, column=3, padx=5, pady=5)
        self.fraction.grid(row=0, column=4, padx=5, pady=5)


if __name__ == '__main__':
    FloatToy.main()
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.