Tkinker - при размещении кнопок внутри рамки = AttributeError: объект не имеет атрибута 'tk' - PullRequest
0 голосов
/ 05 августа 2020

Я пытаюсь создать более сложный GUI для своего приложения. Я пытаюсь разместить кнопки с помощью .grid () внутри фрейма. Однако я получаю "AttributeError: object has no attribute 'tk'" всякий раз, когда пытаюсь создать кнопку с рамкой как root. Я видел, как люди пишут GUI классов (т.е. class Frame (tk.Frame)), и это создало больше проблем с моим кодом. Как я могу создавать кнопки и размещать их во фрейме без необходимости переписывать большинство моих классов с нуля? Однако, если он привязан к «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

Ответы [ 2 ]

1 голос
/ 05 августа 2020

Это помогает, если ваш базовый класс действительно расширяет 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 как super для BaseBlock, все ваши Button, Label и Text сломаются. Извлеченный урок: не говорите, что несколько разных типов виджетов в конечном итоге расширят одно и то же.

Если вы абсолютно застряли на своем пути ~ вы можете сделать это вот так

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)

, а затем, когда вы хотите использовать его как master для Button, используйте 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
0 голосов
/ 05 августа 2020

Проще говоря, вы не можете поместить виджет внутри action_frame, потому что action_frame не является виджетом. Вы можете использовать только виджеты tkinter в качестве мастера другого виджета.

...