首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >数字系统转换器GUI

数字系统转换器GUI
EN

Code Review用户
提问于 2021-11-02 15:25:50
回答 1查看 241关注 0票数 3

这个程序从数字系统A转换为数字系统B,一旦你在其中输入一些东西,作为一个新手,它需要大量的思考,使它的工作无缝和傻瓜的方式。

它将数字从基数A转换为基数10,然后转换为基数B,因为公式总是相同的。

它是基于这个网站的版本:https://codebeautify.org/all-number-converter

尽管如此,我仍然希望在这里发布代码,以获得关于它的结构方式的反馈,这样我就不会养成一些坏习惯:

代码语言:javascript
复制
import tkinter as tk
from tkinter import ttk


class MainApplication:
    numtoletter = {
        0: "0",
        1: "1",
        2: "2",
        3: "3",
        4: "4",
        5: "5",
        6: "6",
        7: "7",
        8: "8",
        9: "9",
        10: "A",
        11: "B",
        12: "C",
        13: "D",
        14: "E",
        15: "F",
        16: "G",
        17: "H",
        18: "I",
        19: "J",
        20: "K",
        21: "L",
        22: "M",
        23: "N",
        24: "O",
        25: "P",
        26: "Q",
        27: "R",
        28: "S",
        29: "T",
        30: "U",
        31: "V",
        32: "W",
        33: "X",
        34: "Y",
        35: "Z",
    }

    lettertonum = {
        "0": 0,
        "1": 1,
        "2": 2,
        "3": 3,
        "4": 4,
        "5": 5,
        "6": 6,
        "7": 7,
        "8": 8,
        "9": 9,
        "A": 10,
        "B": 11,
        "C": 12,
        "D": 13,
        "E": 14,
        "F": 15,
        "G": 16,
        "H": 17,
        "I": 18,
        "J": 19,
        "K": 20,
        "L": 21,
        "M": 22,
        "N": 23,
        "O": 24,
        "P": 25,
        "Q": 26,
        "R": 27,
        "S": 28,
        "T": 29,
        "U": 30,
        "V": 31,
        "W": 32,
        "X": 33,
        "Y": 34,
        "Z": 35,
    }

    numbercheck = {
        2: "01",
        3: "012",
        4: "0123",
        5: "01234",
        6: "012345",
        7: "0123456",
        8: "01234567",
        9: "012345678",
        10: "0123456789",
        11: "0123456789A",
        12: "0123456789AB",
        13: "0123456789ABC",
        14: "0123456789ABCD",
        15: "0123456789ABCDE",
        16: "0123456789ABCDEF",
        17: "0123456789ABCDEFG",
        18: "0123456789ABCDEFGH",
        19: "0123456789ABCDEFGHI",
        20: "0123456789ABCDEFGHIJ",
        21: "0123456789ABCDEFGHIJK",
        22: "0123456789ABCDEFGHIJKL",
        23: "0123456789ABCDEFGHIJKLM",
        24: "0123456789ABCDEFGHIJKLMN",
        25: "0123456789ABCDEFGHIJKLMNO",
        26: "0123456789ABCDEFGHIJKLMNOP",
        27: "0123456789ABCDEFGHIJKLMNOPQ",
        28: "0123456789ABCDEFGHIJKLMNOPQR",
        29: "0123456789ABCDEFGHIJKLMNOPQRS",
        30: "0123456789ABCDEFGHIJKLMNOPQRST",
        31: "0123456789ABCDEFGHIJKLMNOPQRSTU",
        32: "0123456789ABCDEFGHIJKLMNOPQRSTUV",
        33: "0123456789ABCDEFGHIJKLMNOPQRSTUVW",
        34: "0123456789ABCDEFGHIJKLMNOPQRSTUVWX",
        35: "0123456789ABCDEFGHIJKLMNOPQRSTUVWXY",
        36: "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ",
    }

    base = ["Base-2 (Binary)", "Base-3", "Base-4", "Base-5", "Base-6", "Base-7", "Base-8 (Octal)", "Base-9", "Base-10 (Decimal)", "Base-11", "Base-12", "Base-13", "Base-14", "Base-15", "Base-16 (Hexadecimal)", "Base-17",
            "Base-18", "Base-19", "Base-20", "Base-21", "Base-22", "Base-23", "Base-24", "Base-25", "Base-26", "Base-27", "Base-28", "Base-29", "Base-30", "Base-31", "Base-32", "Base-33", "Base-34", "Base-35", "Base-36", ]

    inputnum = "" # number to be converted
    outputnum = "" # result of the conversion
    inputbase = "" # base of the inputnum
    outputbase = "" # base of the outputnum
    outputnumto = 0 # where the outputnum goes
    varx = 0 # to make it so the function "convert" doesn't run when the value of stringvar is changed not through user input
    vary = 0 # to make it so the "convert" function doesn't run infinitely at line 233

    def __init__(self, master): # setting up the window and calling functions for gui creation
        self.master = master
        self.master.title("Number system converter")
        self.master.geometry("600x200")
        self.master.resizable(False, False)

        self.label_title, self.label_equals = self.create_text()
        self.stringvar_number1, self.entry_number1, self.stringvar_number2, self.entry_number2 = self.create_number_input()
        self.combobox_base1, self.combobox_base2 = self.create_base_selection()

    def create_text(self):
        label_title = tk.Label(self.master, text="NUMBER SYSTEM CONVERTER", font=(
            "Calibri", 20, "bold"), anchor=tk.CENTER,)
        label_title.place(x=100, y=0, width=400, height=50)

        label_equals = tk.Label(self.master, text="=", font=(
            "Calibri", 20), anchor=tk.CENTER, justify=tk.CENTER)
        label_equals.place(x=275, y=65, width=50, height=30)

        return label_title, label_equals

    def create_number_input(self): # entry(s) for input and stringvars
        stringvar_number1 = tk.StringVar()
        stringvar_number1.set("1")
        stringvar_number1.trace("w", lambda x, y, z: self.convert(2))

        entry_number1 = tk.Entry(self.master, textvariable=stringvar_number1, font=(
            "Calibri", 16), borderwidth=0, bg="white",)
        entry_number1.place(x=50, y=65, width=225, height=30)

        stringvar_number2 = tk.StringVar()
        stringvar_number2.set("1")
        stringvar_number2.trace("w", lambda x, y, z: self.convert(1))

        entry_number2 = tk.Entry(self.master, textvariable=stringvar_number2, font=(
            "Calibri", 16), borderwidth=0, bg="white",)
        entry_number2.place(x=325, y=65, width=225, height=30)

        return stringvar_number1, entry_number1, stringvar_number2, entry_number2

    def create_base_selection(self): # combobox(s) for selecting the base
        combobox_base1 = ttk.Combobox(
            self.master, values=self.base, font=("Calibri", 14), state="readonly")
        combobox_base1.place(x=50, y=100, width=225, height=30)

        popdown_base1 = self.master.tk.call(
            "ttk::combobox::PopdownWindow", combobox_base1)
        self.master.tk.call(f"{popdown_base1}.f.l",
                            "configure", "-font", "Calibri 14")

        combobox_base1.current(0)

        combobox_base1.bind("<>",
                            lambda x: self.inputbase_or_outputbase())

        combobox_base2 = ttk.Combobox(
            self.master, values=self.base, font=("Calibri", 14), state="readonly")
        combobox_base2.place(x=325, y=100, width=225, height=30)

        popdown_base2 = self.master.tk.call(
            "ttk::combobox::PopdownWindow", combobox_base2)
        self.master.tk.call(f"{popdown_base2}.f.l",
                            "configure", "-font", "Calibri 14")

        combobox_base2.current(0)

        combobox_base2.bind("<>",
                            lambda x: self.inputbase_or_outputbase())

        return combobox_base1, combobox_base2

    def inputbase_or_outputbase(self): # determining if a base belongs to the input or output number
        if self.outputnumto == 2:
            self.inputbase = self.combobox_base1.get()[5:7]
            self.outputbase = self.combobox_base2.get()[5:7]
        elif self.outputnumto == 1:
            self.inputbase = self.combobox_base2.get()[5:7]
            self.outputbase = self.combobox_base1.get()[5:7]

        if self.vary == 0:
            self.convert(self.outputnumto)

    def convert(self, n): # determining which entry modified last (through stringvar tracing) + the process of converting input number in output base (outputbase determined through the previous function)
        if self.varx == 1:
            self.varx = 0
            return

        self.outputnumto = n

        if self.outputnumto == 2:
            self.inputnum = self.entry_number1.get().upper()

        elif self.outputnumto == 1:
            self.inputnum = self.entry_number2.get().upper()

        self.vary = 1
        self.inputbase_or_outputbase()
        self.vary = 0

        if not self.inputbase or not self.outputbase:
            return

        for x in self.inputnum:
            if not x in self.numbercheck[int(self.inputbase)]:
                if self.outputnumto == 1:
                    self.varx = 1
                    self.stringvar_number1.set("Invalid input")
                elif self.outputnumto == 2:
                    self.varx = 1
                    self.stringvar_number2.set("Invalid input")
                return

        base10num = self.convert_to_base10(self.inputbase, self.inputnum)
        outputnum = self.convert_to_baseo(self.outputbase, base10num)

        if self.outputnumto == 1:
            self.varx = 1
            self.stringvar_number1.set(outputnum)
        elif self.outputnumto == 2:
            self.varx = 1
            self.stringvar_number2.set(outputnum)

    def convert_to_base10(self, inputbase, inputnum): # converting inputnum to base10
        inputbase = int(inputbase)
        inputnum = inputnum[::-1]
        multiply = 1
        base10num = 0

        for x in inputnum:
            x = self.lettertonum[x]
            base10num = (x * multiply) + base10num
            multiply = multiply * inputbase

        return base10num

    def convert_to_baseo(self, outputbase, base10num): # converting inputnum to outputbase
        outputbase = int(outputbase)
        base10num = int(base10num)
        outputnum = ""

        while base10num > 0:
            outputnum = outputnum + self.numtoletter[base10num % outputbase]
            base10num = base10num // outputbase

        outputnum = outputnum[::-1]

        return outputnum


if __name__ == "__main__":
    root = tk.Tk()
    app = MainApplication(root)
    root.mainloop()
EN

回答 1

Code Review用户

发布于 2021-11-03 02:50:32

不按特定顺序:

  • numtoletter和类似的变量和方法名应该是num_to_letter (lower_snake_case)
  • numtoletterlettertonum不应该写成那样,而应该通过循环形成--这是计算机所擅长的。
  • 如果您收听来自numbercheckValueErrors,则不必使用int()
  • base也应该通过循环形成。
  • 在类的静态范围中初始化了一组符号。在那里和全局范围之间移动常量是有争议的,但是inputnum通过varx不应该存在。
  • 不要管事情master
  • 您已经接受了构造函数的父部件参数--这很好。理论上,这意味着您可以用转换器填充任何小部件。但是,这也意味着类实例不拥有父实例,因此它不应该负责设置标题和几何图形。
  • 您的标题标签是不必要的-您已经在父窗口装饰器上设置了标题。
  • 一般情况下,您应该避免使用place,而应该选择类似网格的位置。这是不那么脆弱和手动。如果你愿意的话,你还可以添加填充物等等。
  • 你的一些视觉修改,如巨型字体,并不那么有用。tkinter中的通用UI缩放是有点乱,仍然需要您修改字体;但我的看法是:用户可以控制他们的窗口管理器,而且所有自重的窗口管理器都有某种实用工具可以应用缩放。
  • 您不需要保存对任何小部件的引用--只是保存变量。
  • 在构造时,应该始终将父参数、name参数和value参数传递给tk变量。这将避免您第一次调用set
  • 避免吃羊肉。您可以在类上为跟踪创建一个绑定方法。不要伪造xyz的名字,它们实际上是nameindexmode
  • 当tk将组合框的背景颜色设置为灰色时,它试图传达一些信息:组合框的输入部分是只读的,只能通过下拉菜单进行更改。我的观点是,将其设置为白色是令人困惑的,而默认设置应该保持不变。
  • 你在你的输入上使用了StringVar -很好!你应该对你的组合盒做同样的事!这将避免您调用current()
  • 根本不要打电话给bind。这整个输入或输出基础和最后已知的编辑输入的机器太复杂了。您可以知道哪个输入是刚刚编辑的,哪个是调用跟踪的;并且假设在左侧自动转换右侧值的基础上的更改是足够直观的,所以我认为做任何更复杂的事情都没有用。
  • n参数仅为两个值中的一个,因此我建议将其更改为forward: bool。或者,如我所示,只接受一个源和目标组对象,该对象保存对数字和基变量的引用。
  • 添加PEP484类型提示。
  • trace()是不推荐的,应该是trace_add()

建议

代码语言:javascript
复制
from string import ascii_uppercase
import tkinter as tk
from tkinter import ttk
from typing import Tuple, Iterator, Dict, List, Callable


def base_pairs() -> Iterator[Tuple[str, int]]:
    for base in range(2, 37):
        desc = f'Base-{base}'
        name = BASE_NAMES.get(base)
        if name is not None:
            desc += f' ({name})'
        yield desc, base


BASE_NAMES = {
    2: 'Binary',
    8: 'Octal',
    10: 'Decimal',
    16: 'Hexadecimal',
}
BASES: Dict[str, int] = dict(base_pairs())
DEFAULT_BASE = next(iter(BASES.keys()))
NUM_TO_LETTER: List[str] = [
    *(str(x) for x in range(10)),
    *ascii_uppercase,
]


def convert_to_base(output_base: int, base10_num: int) -> str:
    """converting inputnum to outputbase"""
    output_num = ''

    while base10_num > 0:
        base10_num, remainder = divmod(base10_num, output_base)
        output_num = NUM_TO_LETTER[remainder] + output_num

    return output_num


class InputGroup:
    def __init__(
        self, parent: tk.Tk, index: int,
        convert: Callable[[], None],
    ) -> None:
        self.convert = convert

        self.number_var = tk.StringVar(
            parent, name=f'number{index}', value='1',
        )
        self.set_trace()
        tk.Entry(
            parent, textvariable=self.number_var,
        ).grid(row=0, column=2*index)

        self.base_var = tk.StringVar(
            parent, name=f'base{index}', value=DEFAULT_BASE,
        )
        self.base_var.trace_add(mode='write', callback=self.trace)

        names = tuple(BASES.keys())
        ttk.Combobox(
            parent, values=names, state='readonly',
            textvariable=self.base_var,
        ).grid(row=1, column=2*index)

    def set_trace(self) -> None:
        self.trace_id = self.number_var.trace_add(mode='write', callback=self.trace)

    def trace(self, name: str, index: str, mode: str) -> None:
        self.convert()

    @property
    def base(self) -> int:
        return BASES[self.base_var.get()]

    @property
    def value(self) -> int:
        value = self.number_var.get().upper()
        return int(value, self.base)

    @value.setter
    def value(self, value: int) -> None:
        as_base = convert_to_base(self.base, value)
        self.set_output(as_base)

    def set_output(self, value: str) -> None:
        self.number_var.trace_remove(mode='write', cbname=self.trace_id)
        try:
            self.number_var.set(value)
        finally:
            self.set_trace()


class MainApplication:
    def __init__(self, parent: tk.Tk) -> None:
        """setting up the window and calling functions for gui creation"""
        self.parent = parent

        tk.Label(self.parent, text='=').grid(row=0, column=1)

        self.groups = (
            InputGroup(parent, index=0, convert=self.convert_forward),
            InputGroup(parent, index=1, convert=self.convert_backward),
        )

    def convert_forward(self) -> None:
        self.convert(*self.groups)

    def convert_backward(self) -> None:
        self.convert(*self.groups[::-1])

    def convert(self, source: InputGroup, dest: InputGroup) -> None:
        try:
            dest.value = source.value
        except ValueError:
            dest.set_output('Invalid input')


def main() -> None:
    root = tk.Tk()
    root.title('Number system converter')
    root.resizable(width=False, height=False)
    MainApplication(root)
    root.mainloop()


if __name__ == '__main__':
    main()
票数 1
EN
页面原文内容由Code Review提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://codereview.stackexchange.com/questions/269643

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档