Tkinker - When placing buttons inside a frame = AttributeError: object has no attribute 'tk'

Tkinker - When placing buttons inside a frame = AttributeError: object has no attribute 'tk'

我正在尝试为我的应用构建更复杂的 GUI。我正在尝试使用框架内的 .grid() 放置按钮。但是,每当我尝试创建一个以框架为根的按钮时,我都会得到 "AttributeError: object has no attribute 'tk'" 。我见过有人编写 GUI classes(即 class Frame(tk.Frame)),这给我的代码带来了更多问题。我应该如何创建按钮并将它们放置在框架内,而不必从头开始重写我的大部分 classes?

当我将一个按钮固定到“主”时,它工作正常。但是,如果它植根于“action_frame”,那就是我收到错误的时候。

calculator.py

# -*- coding: utf-8 -*-
import tkinter as tk

from math import *
from classes_GUI import ButtonBlock, LabelBlock, TextBlock, FrameBlock
from classes_calculator import ActionBlock


# Store input number
def storeInput(entry_text, result_text, action, array):
    numb = 0.0

    try:
        numb = float(entry_text.retrieveTextInput())
    except:
        print('Please enter a valid number')
        return

    calc_action = ActionBlock(numb, action)
    array.append(calc_action)
    entry_text.clearText()

    num = calc_action.returnNumber()
    act = calc_action.returnAction()

    input_texts = dict([
        (1, ' + ' + str(num)),
        (2, ' - ' + str(num)),
        (3, ' * ' + str(num)),
        (4, ' / ' + str(num)),
        (5, ' + 1/' + str(num)),
        (6, ' + ' + str(num) + '^2')
        ])

    result_text.changeText(input_texts[act])


# Calculate result
def calcResult(entry_text, result_text, array):
    result = 0.0

    for calc in array:
        action = calc.returnAction()
        num = calc.returnNumber()

        if action == 1:
            result += num
        elif action == 2:
            result -= num
        elif action == 3:
            result *= num
        elif action == 4:
            result /= num
        elif action == 5:
            result += 1.0 / num
        elif action == 6:
            result += num ** 2

    entry_text.clearText()
    result_text.changeText(str(result), True)


# Create a new calculator instance
def exeCalc():
    action_blocks = []
    button_blocks = []
    frame_blocks = []
    label_blocks = []
    text_blocks = []

    # Create GUI
    master = tk.Tk()
    master.title('Calculator')

    # Create frames
    action_frame = FrameBlock(master, 30, 30, 1, 6)
    frame_blocks.append(action_frame)

    for f in frame_blocks:
        f.createFrame()

    # Create GUI labels
    title_label = LabelBlock(master, 20, 2, 'n', 0, 0, 'center', 'Calculator')
    label_blocks.append(title_label)
    entry_label = LabelBlock(master, 20, 2, 'n', 1, 0, 'center', 'Enter:')
    label_blocks.append(entry_label)
    result_label = LabelBlock(master, 20, 2, 'n', 2, 0, 'center', 'Result:')
    label_blocks.append(result_label)

    for l in label_blocks:
        l.createLabel()

    # Create GUI text
    entry_text = TextBlock(master, 20, 2, 1, 1, 'normal', '')
    text_blocks.append(entry_text)
    result_text = TextBlock(master, 20, 2, 2, 1, 'disabled', '0')
    text_blocks.append(result_text)

    for t in text_blocks:
        t.createText()

    # Create GUI buttons
    close_button = ButtonBlock(master, 6, 2, 3, 0, 'Close',
        lambda: master.destroy())
    button_blocks.append(close_button)

    add_button = ButtonBlock(frame_blocks[0], 4, 2, 0, 0, '+',
        lambda: storeInput(text_blocks[0], text_blocks[1], 1, action_blocks))
    button_blocks.append(add_button)

    subtract_button = ButtonBlock(frame_blocks[0], 4, 2, 0, 1, '-',
        lambda: storeInput(text_blocks[0], text_blocks[1], 2, action_blocks))
    button_blocks.append(subtract_button)

    multiply_button = ButtonBlock(frame_blocks[0], 4, 2, 0, 2, 'x',
        lambda: storeInput(text_blocks[0], text_blocks[1], 3, action_blocks))
    button_blocks.append(multiply_button)

    divide_button = ButtonBlock(frame_blocks[0], 4, 2, 1, 0, '/',
        lambda: storeInput(text_blocks[0], text_blocks[1], 4, action_blocks))
    button_blocks.append(divide_button)

    fraction_button = ButtonBlock(frame_blocks[0], 4, 2, 1, 1, '1/x',
        lambda: storeInput(text_blocks[0], text_blocks[1], 5,
        action_blocks))
    button_blocks.append(fraction_button)

    square_block = ButtonBlock(frame_blocks[0], 4, 2, 1, 2, 'x^2',
        lambda: storeInput(text_blocks[0], text_blocks[1], 6,
        action_blocks))
    button_blocks.append(square_block)

    equal_button = ButtonBlock(frame_blocks[0], 4, 2, 2, 0, '=',
        lambda: calcResult(text_blocks[0], text_blocks[1], action_blocks))
    button_blocks.append(equal_button)

    for b in button_blocks:
        b.createButton()

    master.mainloop()

classes_GUI.py

# -*- coding: utf-8 -*-
import tkinter as tk


# Create a base data block
class BaseBlock():
    def __init__(self, root, width, height, txt):
        self.root = root
        self.width = width
        self.height = height
        self.txt = txt


# Create a inner data block
class InnerBlock(BaseBlock):
    def __init__(self, root, width, height, row, column, txt):
        super().__init__(root, width, height, txt)
        self.g_row = row
        self.g_column = column


# Create a data block for a button
class ButtonBlock(InnerBlock):
    def __init__(self, root, width, height, row, column, txt, command=None):
        super().__init__(root, width, height, row, column, txt)
        self.command = command

    def createButton(self):
        button = tk.Button(self.root, text=self.txt, width=self.width,
            height=self.height, command=self.command)
        button.grid(row=self.g_row, column=self.g_column)
        return button


# Create a frame data block
class FrameBlock(InnerBlock):
    def __init__(self, root, width, height, row, column, txt=None):
        super().__init__(root, width, height, row, column, txt)

    def createFrame(self):
        frame = tk.Frame(self.root, width=self.width, height=self.height)
        frame.grid(row=self.g_row, column=self.g_column)
        return frame


# Create a data block for a  window
class LabelBlock(InnerBlock):
    def __init__(self, root, width, height, anchor, row, column, justify, txt):
        super().__init__(root, width, height, row, column, txt)
        self.anchor = anchor
        self.justify = justify

    def createLabel(self):
        label = tk.Label(self.root, width=self.width, height=self.height,
            anchor=self.anchor, justify=self.justify, text=self.txt)
        label.grid(row=self.g_row, column=self.g_column)
        return label


# Create a data block for text
class TextBlock(InnerBlock):
    def __init__(self, root, width, height, row, column, state, txt):
        super().__init__(root, width, height, row, column, txt)
        self.state = state
        self.text = None

    def createText(self):
        self.text = tk.Text(self.root, width=self.width, height=self.height)
        self.text.insert(tk.END, self.txt)
        self.text.grid(row=self.g_row, column=self.g_column)
        self.text.config(state=self.state)
        return self.text

    # Clear text
    def clearText(self):
        self.text.delete('1.0', 'end')

    # Change text
    def changeText(self, new_txt, clear=False):
        self.text.config(state='normal')
        if clear:
            self.clearText()
        self.text.insert(tk.END, new_txt)
        self.text.config(state='disabled')

    # Retrieve input from text box
    def retrieveTextInput(self):
        text_input = self.text.get('1.0', 'end')
        return text_input

如果你的基础 class 实际上扩展了一个 Frame,它会有所帮助。 ;)

class BaseBlock(tk.Frame):
    def __init__(self, master, width, height, txt):
        tk.Frame.__init__(self, master)

但实际上,您创建了太多层并且管理一切都很奇怪。下面的会更好。所有这些每一步的继承和创建功能都太混乱了。

import tkinter as tk
from math import *
from dataclasses import asdict, dataclass
from typing import Callable  
  
@dataclass
class Label_dc:
    width:   int = 20
    height:  int = 2
    anchor:  str = 'n'
    justify: str = 'center'
    text:    str = ''

    
@dataclass
class Button_dc:
    width:   int      = 4
    height:  int      = 2
    text:    str      = ''
    command: Callable = None    


@dataclass
class Text_dc:
    width:  int = 20
    height: int = 2
    state:  str = 'normal'

    
#from classes_calculator import ActionBlock
class FrameBlock(tk.Frame):
    def __init__(self, master, row, column, rowspan, **kwargs):
        tk.Frame.__init__(self, master, **kwargs)
        
        self.grid(row=row, column=column, rowspan=rowspan)

       
class ButtonBlock(tk.Button):
    def __init__(self, master, row, column, **kwargs):
        tk.Button.__init__(self, master, **asdict(Button_dc(**kwargs)))
        
        self.grid(row=row, column=column)

        
class LabelBlock(tk.Label):
    def __init__(self, master, row, column, **kwargs):
        tk.Label.__init__(self, master, **asdict(Label_dc(**kwargs)))
        
        self.grid(row=row, column=column)


class TextBlock(tk.Text):
    def __init__(self, master, row, column, text='', **kwargs):
        tk.Text.__init__(self, master, **asdict(Text_dc(**kwargs)))
        
        self.grid(row=row, column=column)
        self.insert('1.end', text)

    # Clear text
    def clearText(self):
        self.delete('1.0', 'end')

    # Change text
    def changeText(self, new_txt, clear=False):
        self.config(state='normal')
        if clear:
            self.clearText()
        self.insert(tk.END, new_txt)
        self.config(state='disabled')

    # Retrieve input from text box
    def retrieveTextInput(self):
        return self.get('1.0', 'end')


class App(tk.Tk):
    WIDTH  = 800
    HEIGHT = 600

    def __init__(self, *args, **kwargs):
        tk.Tk.__init__(self, *args, **kwargs)
    
        # Create GUI labels
        LabelBlock(self, 0, 0, text='Calculator')
        LabelBlock(self, 1, 0, text='Enter:')
        LabelBlock(self, 2, 0, text='Result:')
        
        # Create GUI text
        text_blocks = {
            'entry' : TextBlock(self, 1, 1),
            'result': TextBlock(self, 2, 1, state='disabled', text='0'),
        }
    
        #can't use ButtonBlock for this one ~ self.destroy wont pickle properly
        tk.Button(self, text='Close', width=6, height=2, command=self.destroy).grid(row=3, column=0)
        
        action = []
    
        # Create frames
        frame = FrameBlock(self, 1, 3, 2, width=30, height=30)
        
        # Create GUI buttons
        ButtonBlock(frame, 0, 0, text='+', command=lambda: self.store(*text_blocks, 1, action))
        ButtonBlock(frame, 0, 1, text='-', command=lambda: self.store(*text_blocks, 2, action))
        ButtonBlock(frame, 0, 2, text='*', command=lambda: self.store(*text_blocks, 2, action))
        ButtonBlock(frame, 1, 0, text='/', command=lambda: self.store(*text_blocks, 4, action))
        ButtonBlock(frame, 1, 1, text='1/x', command=lambda: self.store(*text_blocks, 5, action))
        ButtonBlock(frame, 1, 2, text='x^2', command=lambda: self.store(*text_blocks, 6, action))
        ButtonBlock(frame, 2, 0, text='=', command=lambda: self.calc(*text_blocks, action))

    def store(self, entry, result, action, array):
        pass #remove this line
        numb = 0.0
    
        try:
            numb = float(entry.retrieveTextInput())
        except:
            print('Please enter a valid number')
            return
    
        calc_action = ActionBlock(numb, action)
        array.append(calc_action)
        entry.clearText()
    
        num = calc_action.returnNumber()
        act = calc_action.returnAction()
    
        input_texts = dict([
            (1, ' + ' + str(num)),
            (2, ' - ' + str(num)),
            (3, ' * ' + str(num)),
            (4, ' / ' + str(num)),
            (5, ' + 1/' + str(num)),
            (6, ' + ' + str(num) + '^2')
            ])
    
        result.changeText(input_texts[act])
        
    # Calculate result
    def calc(self, entry, result, array):
        pass #remove this line
        r = 0.0
    
        for calc in array:
            action = calc.returnAction()
            num = calc.returnNumber()
    
            if action == 1:
                result += num
            elif action == 2:
                result -= num
            elif action == 3:
                result *= num
            elif action == 4:
                result /= num
            elif action == 5:
                result += 1.0 / num
            elif action == 6:
                result += num ** 2
    
        entry.clearText()
        result.changeText(str(r), True)


if __name__ == '__main__':
    app = App()
    app.title("Calculator")
    app.geometry(f'{App.WIDTH}x{App.HEIGHT}')
    app.mainloop()

改变你所拥有的一切都是值得的。一切都会变得更干净,更易于管理。另外,我刚刚为你做了很多,你的方法永远行不通。一旦您使用 Frame 作为 BaseBlocksuper,您的 ButtonLabelText 将全部中断。经验教训:不要告诉一堆不同类型的小部件最终扩展相同的东西。


如果你绝对坚持按照自己的方式去做~你可以这样做

class FrameBlock(InnerBlock):
    def __init__(self, root, width, height, row, column, txt=None):
        super().__init__(root, width, height, row, column, txt)
        self.frame = tk.Frame(self.root, width=self.width, height=self.height)
        self.frame.grid(row=self.g_row, column=self.g_column)

然后当您想将它用作 Buttonmaster 时,请使用 action_frame.frame


一边

你计算结果的方法根本行不通。您甚至没有考虑运算符优先级。使用 eval()。向您展示您还有多远......这就是解析 python 支持的每个可以想象的数学表达式所需要的。即使您将其简化为您的计算器支持的内容,它仍然会比您当前的整个应用程序大。

class Expression:
    # Clean
    __WHITE: str = '\s'
    __white: Pattern = re.compile(__WHITE)

    __COMM: str = '#\s.*$'
    __comm: Pattern = re.compile(__COMM)

    # Symbolic
    __PARENS: str = '[\)\(]'
    __parens: Pattern = re.compile(__PARENS)

    __INFIX: str = '[%&+-]|[*/]{1,2}|<<|>>|\||\^'
    __infix: Pattern = re.compile(__INFIX)

    __TOKEN: str = 'STK([0-9]+)'
    __token: Pattern = re.compile(__TOKEN)

    __SYMBOLIC: str = f'{__PARENS}|{__INFIX}'

    # Prefix
    __INV: str = '([~]+|~u)?'

    # Numeric
    __HEX: str = '([-]?0x[0-9a-f]+)'
    __hex: Pattern = re.compile(__HEX)
    __IHEX: str = f'{__INV}{__HEX}'
    __ihex: Pattern = re.compile(__IHEX)
    __OHEX: str = f'^{__HEX}$'
    __ohex: Pattern = re.compile(__OHEX)

    __NUM: str = '([-]?[0-9]+(\.[0-9]+)?)'
    __num: Pattern = re.compile(__NUM)
    __INUM: str = f'{__INV}{__NUM}'
    __inum: Pattern = re.compile(__INUM)
    __ONUM: str = f'^{__NUM}$'
    __onum: Pattern = re.compile(__ONUM)

    __NUMERIC: str = f'{__IHEX}|{__INUM}'

    # Variable
    __HYPER: str = 'acosh|asinh|atanh|cosh|sinh|tanh'
    __TRIG: str = 'acos|asin|atan2|atan|cos|sin|tan|hypot|dist'
    __THEORY: str = 'ceil|comb|fabs|factorial|floor|fmod|frexp|gcd|isqrt|ldexp|modf|perm|remainder|trunc'
    __LOG: str = 'expm1|exp|log1p|log10|log2|log|pow|sqrt'
    __ANGLE: str = 'degrees|radians'
    __SPEC: str = 'erfc|erf|lgamma|gamma'
    __FN: str = f'{__HYPER}|{__TRIG}|{__THEORY}|{__LOG}|{__ANGLE}|{__SPEC}'
    __func: Pattern = re.compile(__FN)

    __RAND: str = '(random|rand)'
    __rand: Pattern = re.compile(__RAND)

    __CONST: str = 'pi|e|tau|inf|' + __RAND
    __const: Pattern = re.compile(__CONST)

    __BITWISE: str = '<<|>>|\||\^|&'
    __bitwise: Pattern = re.compile(__BITWISE)

    __FN2: str = 'min|max|' + __RAND
    __func2: Pattern = re.compile(__FN2)

    __VARIABLE: str = f'{__FN}|{__FN2}|{__CONST}'

    __SIMPLE: str = f'^({__INUM}+{__INFIX})+{__INUM}$'
    __simple: Pattern = re.compile(__SIMPLE)

    # Combo
    __MATH: str = f'{__VARIABLE}|{__NUMERIC}|{__SYMBOLIC}|,|E|\s'
    __math: Pattern = re.compile(__MATH)

    # Priorities
    __P1: str = '[*/]{1,2}|%'
    __P2: str = '[+-]'
    __P3: str = '<<|>>|&'
    __P4: str = '\||\^'
    __priority: List[Pattern] = [re.compile(__P1), re.compile(__P2), re.compile(__P3), re.compile(__P4)]

    def __init__(self):
        self.value = math.nan

    def evaluate(self, expr: str) -> float:
        self.value = Expression.eval(expr)
        return self.value

    @staticmethod
    def __hexrepl(m: Match[Union[str, bytes]]):
        return str(int(m.group(0), 16))

    @staticmethod
    def eval(expr: str, fast: bool = False) -> float:
        # Remove Whitespace, Comments, Convert Hash To Hex and Case To Lower
        expr = Expression.__comm.sub("", expr)
        expr = Expression.__white.sub("", expr)
        expr = expr.replace('#', '0x').lower()

        # Check If This Is Actual Math By Deleting Everything Math Related And Seeing If Anything Is Left
        if len(re.sub(Expression.__math, "", expr)) > 0:
            return math.nan

        if fast:
            return Expression.__fast(expr)

        # Parse All Inversions Now ... invert(~) is the only "left side only" operator
        expr = Expression.__parse_inversions(expr)
        expr = Expression.__hex.sub(Expression.__hexrepl, expr)

        # Check If This Is Solely A Number ~ If So, Parse Int And Return
        if Expression.__onum.match(expr):
            n = float(expr)
            return int(n) if n % 1 == 0 else n

        # We Got This Far. It Must Be Math
        n = Expression.__parse(expr)
        return int(n) if n % 1 == 0 else n

    # Private Static Interfaces

    @staticmethod
    def __parse_inversions(expr: str) -> str:
        match: Iterator[Match[Union[str, bytes]]] = Expression.__ihex.finditer(expr)
        m: Match[Union[str, bytes]]
        for m in match:
            expr = Expression.__invert_expr(expr, m, 16)
        match = Expression.__inum.finditer(expr)
        for m in match:
            expr = Expression.__invert_expr(expr, m, 10)
        return expr

    @staticmethod
    def __invert_expr(expr: str, m: Match[Union[str, bytes]], b: int) -> str:
        t1: str = m.group(1)
        t2: str = m.group(2)
        if t1:
            if t1 == '~u':
                n: int = Expression.__uinvert_num(int(t2, b))
            else:
                f: int = len(t1) % 2 == 1
                n: int = -(int(t2, b) + 1) if f else int(t2, b)
            expr = expr.replace(m.group(0), str(n))
        return expr

    @staticmethod
    def __uinvert_num(num: float) -> int:
        if num > 0:
            x: int = int(math.log(num, 2.0) + 1)
            i: int = 0
            for i in range(0, x):
                num = (num ^ (1 << i))
        return num

    @staticmethod
    def __parse(expr: str) -> float:
        exp_stack: List[str] = []
        ops_stack: List[str] = []
        res_stack: List[float] = []

        tokens = Expression.__tokenize(expr)

        # everything that can come before an operator
        b1: str = f'{Expression.__HEX}|{Expression.__NUM}|{Expression.__CONST}|\)'
        c: Pattern = re.compile(b1)

        # before an operator that is the rest of this expression
        b2: str = f'{Expression.__NUM}E'
        d: Pattern = re.compile(b2, re.I)

        expr = tokens.expression[0::]

        while len(expr):
            m: Match[Union[str, bytes]] = Expression.__infix.search(expr)
            if m:
                op: str = m.group()
                left: str = expr[0:m.span()[0]]
                if re.search(c, left) and not re.search(d, left):
                    exp_stack.append(left)
                    ops_stack.append(op)
                    expr = expr.replace(f'{left}{op}', "")
                else:
                    if len(left) == 0 or re.match(d, left):
                        right: str = expr[m.span()[1]::]
                        m = Expression.__infix.search(right)
                        if m:
                            left = f'{left}{op}'
                            op = m.group()
                            left = f'{left}{right[0:m.span()[0]]}'
                            exp_stack.append(left)
                            ops_stack.append(op)
                            expr = expr.replace(f'{left}{op}', "")
                        else:
                            exp_stack.append(expr)
                            expr = ""
                    else:
                        # Probably Not Even Possible In A Valid Math Expression
                        print("Expression.parse(expr:String): unexpected left side")
                        print("expression: ", expr)
                        print("left side: ", left)
                        print("operator: ", op)
                        print("exp_stack: ", exp_stack)
                        print("ops_stack: ", ops_stack)
            else:
                exp_stack.append(expr)
                expr = ""

        for r in range(len(exp_stack)):
            m: Match[Union[str, bytes]] = Expression.__token.search(exp_stack[r])
            inner: str = ""
            if m:
                i: int = int(m.group(1))
                inner = tokens.stack[i]

            res_stack.append(Expression.__parsetype(exp_stack[r], inner))

        # Iterate Through Stacks Based On Priority and Do Assignments ~ ie... Calculate Everything
        if len(ops_stack) > 0:
            p: int = 0
            for p in range(len(Expression.__priority)):
                n: int = 0
                while n < len(ops_stack) and len(ops_stack) > 0:
                    m: Match[Union[str, bytes]] = Expression.__priority[p].match(ops_stack[n])
                    if m is not None:
                        if not math.isnan(res_stack[n]) and not math.isnan(res_stack[n + 1]):
                            res_stack[n] = Expression.__value(res_stack[n], ops_stack[n], res_stack[n + 1])
                            res_stack.pop(n + 1)
                            ops_stack.pop(n)
                    else:
                        n += 1

        return res_stack[0] if len(res_stack) == 1 else math.nan

    @staticmethod
    def __parsetype(expr: str, val: str = "") -> float:
        fin: float = math.nan

        if val != "":
            tokens: Tokens_t = Expression.__tokenize(val)
            csv: List[str] = tokens.expression.split(",")
            a: float = 0
            b: float = 0
            f: str = ""
            ln: int = len(csv)

            if ln >= 1:
                a = Expression.__parse(Expression.__detokenize(csv[0], tokens))
            if ln == 2:
                b = Expression.__parse(Expression.__detokenize(csv[1], tokens))

            m: Match[Union[str, bytes]] = Expression.__func.match(expr)
            m2: Match[Union[str, bytes]] = Expression.__func2.match(expr)
            if m:
                f = m.group()
                fin = getattr(math, f)(a, b) if len(csv) == 2 else getattr(math, f)(a)
            elif m2:
                f = m2.group()
                if ln == 2:
                    if f == 'min':
                        fin = min(a, b)
                    elif f == 'max':
                        fin = max(a, b)
                elif ln == 1:
                    if Expression.__rand.match(f):
                        fin = random() * a
            else:
                fin = Expression.__parse(val)
        else:
            m: Match[Union[str, bytes]] = Expression.__const.match(expr)
            c: Match[Union[str, bytes]] = Expression.__hex.match(expr)
            if m:
                cn: str = m.group()
                fin = random() if Expression.__rand.match(cn) else getattr(math, cn)
            elif c:
                fin = int(c.group(), 16)
            else:
                fin = float(expr)

        return fin

    @staticmethod
    def __tokenize(expr: str) -> Tokens_t:
        c: int = 0
        b: int = -1
        e: int = -1
        ex: str = expr[0::]
        s: List[str] = []
        m: Match[Union[str, bytes]]
        p: Iterator[Match[Union[str, bytes]]] = Expression.__parens.finditer(ex)
        for m in p:
            if m.group() == "(":
                c += 1
                if b == -1:
                    b = m.span()[1]
            elif m.group() == ")":
                c -= 1
                if c == 0 and b > -1:
                    e = m.span()[0]
                    if b != e:
                        s.append(expr[b:e])
                        ex = ex.replace(expr[b:e], f'STK{len(s) - 1}')
                    b = -1

        return Tokens_t(ex, s)  # Tokens_t ~ python equivalent to my c++ math parser

    @staticmethod
    def __detokenize(part: str, tokens: Tokens_t) -> str:
        ex: str = part[0::]
        m: Match[Union[str, bytes]]
        p: Iterator[Match[Union[str, bytes]]] = Expression.__token.finditer(ex)
        for m in p:
            ex = ex.replace(m.group(0), tokens.stack[int(m.group(1))])
        return ex

    @staticmethod
    def __fast(expr: str) -> float:
        return eval(expr)

    __ops: Dict[str, Callable] = {
        '+': lambda x, y: x + y,
        '-': lambda x, y: x - y,
        '*': lambda x, y: x * y,
        '/': lambda x, y: x / y,
        '**': lambda x, y: x ** y,
        '//': lambda x, y: x // y,
        '>>': lambda x, y: x >> y,
        '<<': lambda x, y: x << y,
        '&': lambda x, y: x & y,
        '|': lambda x, y: x | y,
        '^': lambda x, y: x ^ y,
        '%': lambda x, y: x % y,
    }

    @staticmethod
    def __value(v1: float, oper: str, v2: float) -> float:
        x: float = 0
        try:
            m: Match[Union[str, bytes]] = Expression.__bitwise.match(oper)
            x = Expression.__ops[oper](v1, v2) if not m else Expression.__ops[oper](int(v1), int(v2))
        except KeyError:
            x = math.nan
        return x

简单地说,您不能在 action_frame 中放置小部件,因为 action_frame 不是小部件。您只能将 tkinter 小部件用作另一个小部件的主人。