_tkinter.TclError: cannot use geometry manager grid inside . which already has slaves managed by pack

Не понимаю в чем прикол. Вот код, который есть у меня:

text_lab = Label(self, text="Create Text: ")
text_lab.grid(row=4, column=0, padx=5)
text_entry.pack(side=BOTTOM)

Так он работает. Но если я убираю .pack на .grid, то выдает ошибку, что grid и pack не могут быть вместе. Хотя вместе они работают, а убирая pack перестают. Что я не понимаю ?

text_lab = Label(self, text="Create Text: ")
text_lab.grid(row=4, column=0, padx=5)
text_entry.grid(row=4, column=1)

В таком виде выдает ошибку:

_tkinter.TclError: cannot use geometry manager grid inside . which already has slaves managed by pack.

Полная версия кода:

from tkinter import *
from tkinter import messagebox
from tkinter.tix import *
# import os
# import matplotlib.pyplot as plt
from PIL import Image, ImageTk, ImageGrab
from tkinter.filedialog import askopenfilename, asksaveasfile
import keyboard


class Paint(Frame):

    def __init__(self, parent):
        Frame.__init__(self, parent)
        self.cursor_position_print()

        print(parent)
        self.parent = parent
        self.color = "black"
        self.brush_size = 2

        self.setUI()

    def cursor_position_print(self):
        global x, y
        x = self.winfo_pointerx() - self.winfo_rootx()
        y = self.winfo_pointery() - self.winfo_rooty()
        # print('Позиция курсора: {}, {}'.format(x, y))
        self.after(1, self.cursor_position_print)

    # Поиск координат курсора

    def set_color(self, new_color):
        self.color = new_color

    def set_brush_size(self, new_size):
        self.brush_size = new_size

    def line(self, event):
        global mode, x, y, x1, x2, y1, y2, countLine

        if (mode == 4):

            if (countLine == 1):
                x2 = x - 5
                y2 = y - 110
                countLine = 2
                print('Вторая точка {}:{}'.format(x2, y2))
                paint.canv.create_line(x1, y1, x2, y2, width=self.brush_size, fill=self.color)

            if (countLine == 0):
                x1 = x - 5
                y1 = y - 110
                countLine += 1
                print('Первая точка {}:{}'.format(x1,y1))

            if (countLine == 2):
                countLine = 0

    def rectangle(self, event):

        global mode, x, y
        if (mode == 2):
            x1 = x - self.brush_size - 5
            y1 = y + self.brush_size - 110
            x2 = x + self.brush_size - 5
            y2 = y - self.brush_size - 110
            self.canv.create_rectangle(x1, y1, x2, y2, fill=self.color, outline=self.color)

    def circle(self, event):

        global mode, x, y
        if (mode == 3):
            x1 = x - self.brush_size - 5
            y1 = y + self.brush_size - 110
            x2 = x + self.brush_size - 5
            y2 = y - self.brush_size - 110
            self.canv.create_oval(x1, y1, x2, y2, fill=self.color, outline=self.color)

    def draw(self, event):

        global mode
        if (mode == 1):
            self.canv.create_oval(event.x - self.brush_size,
                                  event.y - self.brush_size,
                                  event.x + self.brush_size,
                                  event.y + self.brush_size,
                                  fill=self.color, outline=self.color)

    # Непосредственно функция, которая будет отвечать за
    # появление "линии" на холсте, если выбрана кисть

    def setScroll(self):
        sw = ScrolledWindow(self, scrollbar=Y)
        sw.pack(fill=BOTH, expand=1)

    def setUI(self):
        global mode, master
        # Создаем переменную, которая в дальнейшем будет отвечать
        # за активный инструмент

        self.pack(fill=BOTH, expand=1)
        # Размещаем активные элементы на родительском окне

        self.columnconfigure(7, weight=1)
        # Даем седьмому столбцу возможность растягиваться, благодаря
        # чему кнопки не будут разъезжаться при ресайзе

        self.rowconfigure(5, weight=1)
        # То же самое для четвертого ряда. Там будет холст

        self.canv = Canvas(self, bg='white')
        # Создаем поле для рисования, устанавливаем белый фон

        self.canv.grid(row=5, column=0, columnspan=8, padx=5, pady=5, sticky=E + W + S + N)
        # Прикрепляем канвас методом grid. Он будет находится в 3м ряду, первой колонке,
        # и будет занимать 7 колонок, задаем отступы по X и Y в 5 пикселей, и заставляем
        # растягиваться при растягивании всего окна

        self.canv.bind("<B1-Motion>", self.draw)
        # Привязываем обработчик к канвасу. <B1-Motion> означает "при движении
        # зажатой левой кнопки мыши" вызывать функцию draw

        color_lab = Label(self, text="Color: ")
        # Создаем метку для кнопок изменения цвета кисти

        color_lab.grid(row=0, column=0, padx=5)
        # Устанавливаем созданную метку в первый ряд и
        # первую колонку, задаем горизонтальный отступ в 6 пикселей

        red_btn = Button(self, text="Red", width=8, command=lambda: self.set_color("red"))
        # Создание кнопки:  Установка текста кнопки, задание ширины
        # кнопки (10 символов), функция вызываемая при нажатии кнопки.

        red_btn.grid(row=0, column=1)
        # Устанавливаем кнопку

        # Создание остальных кнопок повторяет ту же логику, что и создание
        # кнопки установки красного цвета, отличаются лишь аргументы.

        green_btn = Button(self, text="Green", width=8, command=lambda: self.set_color("green"))
        green_btn.grid(row=0, column=2)

        blue_btn = Button(self, text="Blue", width=8, command=lambda: self.set_color("blue"))
        blue_btn.grid(row=0, column=3)

        black_btn = Button(self, text="Black", width=8, command=lambda: self.set_color("black"))
        black_btn.grid(row=0, column=4)

        white_btn = Button(self, text="White", width=8, command=lambda: self.set_color("white"))
        white_btn.grid(row=0, column=5)

        purple_btn = Button(self, text="Purple", width=8, command=lambda: self.set_color("purple"))
        purple_btn.grid(row=0, column=6)

        yellow_btn = Button(self, text="Yellow", width=8, command=lambda: self.set_color("yellow"))
        yellow_btn.grid(row=0, column=7, sticky=W)

        # clear_btn = Button(self, text="Очистить", width=8, command=lambda: self.canv.delete("all"))
        # clear_btn.grid(row=0, column=6, sticky=W)

        size_lab = Label(self, text="Brush size: ")
        size_lab.grid(row=1, column=0, padx=5)

        one_btn = Button(self, text="2", width=8, command=lambda: self.set_brush_size(2))
        one_btn.grid(row=1, column=1)

        two_btn = Button(self, text="5", width=8, command=lambda: self.set_brush_size(5))
        two_btn.grid(row=1, column=2)

        five_btn = Button(self, text="10", width=8, command=lambda: self.set_brush_size(10))
        five_btn.grid(row=1, column=3)

        seven_btn = Button(self, text="20", width=8, command=lambda: self.set_brush_size(20))
        seven_btn.grid(row=1, column=4)

        twenty_btn = Button(self, text="40", width=8, command=lambda: self.set_brush_size(40))
        twenty_btn.grid(row=1, column=5)

        thirty_btn = Button(self, text="50", width=8, command=lambda: self.set_brush_size(50))
        thirty_btn.grid(row=1, column=6)

        activeMode_lab = Label(self, text="Tools: ")
        activeMode_lab.grid(row=2, column=0, padx=5)

        brush_btn = Button(self, text='Brush', width=8, command=lambda:
        [setBrush(), paint.canv.bind("<B1-Motion>", self.draw), print('Brush on')])
        brush_btn.grid(row=2, column=1)

        rectangle_btn = Button(self, text='Square', width=8, command=lambda:
        [setRectangle(), print('Rectangle on'), paint.canv.bind("<ButtonPress-1>", paint.rectangle)])
        rectangle_btn.grid(row=2, column=2)

        oval_btn = Button(self, text='Circle', width=8, command=lambda:
        [setCircle(), print('Circle on'), paint.canv.bind("<ButtonPress-1>", paint.circle)])
        oval_btn.grid(row=2, column=3)

        line_lab = Label(self, text="Create Line: ")
        line_lab.grid(row=3, column=0, padx=5)

        line_btn = Button(self, text='Line', width=8, command=lambda:
        [setLine(), print('Line on'), paint.canv.bind("<ButtonPress-1>", paint.line)])
        line_btn.grid(row=3, column=1)

        infoLine1_lab = Label(self, text="-->")
        infoLine1_lab.grid(row=3, column=4, padx=5)

        infoLine_lab = Label(self, text="First click - First point. Second click - Second Point")
        infoLine_lab.grid(row=3, column=7, padx=5)

        text_lab = Label(self, text="Create Text: ")
        text_lab.grid(row=4, column=0, padx=5)

        text_entry.grid(row=4, column=1)

        # oladka_btn = Button(self, text='mode = ?', width=8, command=lambda: print(mode))
        # oladka_btn.grid(row=3, column=2)


def setBrush():
    global mode
    mode = 1


def setRectangle():
    global mode
    mode = 2


def setCircle():
    global mode
    mode = 3


def setLine():
    global mode, countLine
    mode = 4
    countLine = 0


def importImage():
    global paint
    fname = askopenfilename(filetypes=[("Изображения", "*.jpeg;*.jpg;*.png;*.gif")])

    if fname is not None:
        try:
            img = ImageTk.PhotoImage(Image.open(fname))
            paint.canv.background = img
            paint.canv.create_image(0, 0, anchor=NW, image=img)
        except Exception as err:
            messagebox.showerror("Ошибка загрузки файла", err)
    # Функция импорта изображения на холст


def saveImg():
    global paint

    fname = asksaveasfile(mode='w', defaultextension=".jpg", filetypes=[("JPEG", ".jpg")])
    if fname is None:
        return

    x = paint.canv.winfo_rootx() + 2
    y = paint.canv.winfo_rooty() + 2
    xx = x + paint.canv.winfo_width() - 4
    yy = y + paint.canv.winfo_height() - 4
    ImageGrab.grab(bbox=(x, y, xx, yy)).save(fname)
    # Функция сохранения нарисованного на холсте


def main():
    global paint, mode, master
    mode = 1

    root = Tk()
    root.title('Analoque ImageEditor')
    root.geometry("850x650+250+50")
    # Создание окна, смена его названия, размера и положения
    root.iconbitmap('Icons\Icon.ico')

    mainmenu = Menu(root)
    root.config(menu=mainmenu)

    filemenu = Menu(mainmenu, tearoff=0)
    filemenu.add_command(label="Очистить холст", command=lambda: paint.canv.delete("all"))
    filemenu.add_command(label="Импорт", command=importImage)
    filemenu.add_command(label="Сохранить нарисованное", command=saveImg)
    filemenu.add_command(label="Выход", command=root.quit)

    # helpmenu = Menu(mainmenu, tearoff=0)
    # helpmenu.add_command(label="Помощь")
    # helpmenu.add_command(label="О программе")

    mainmenu.add_cascade(label="Файл", menu=filemenu)
    # mainmenu.add_cascade(label="Справка", menu=helpmenu)
    # Добавление меню и его дальнейшее наполнение с подменю

    global text, text_entry
    text = StringVar()
    text_entry = Entry(textvariable=text)

    paint = Paint(root)
    # Функция, открывающая возможность рисовать

    keyboard.add_hotkey('Ctrl + S', lambda: saveImg())
    # Сохранение нарисованного через сочетание клавиш "Ctrl + S"

    root.mainloop()


if __name__ == '__main__':
    main()

    # Основа, которая запускает main(), если данный файл является
    # главным в запуске

Ответы (1 шт):

Автор решения: insolor

Обратите внимание на эту строку в

text_entry.grid(row=4, column=1)

внутри метода setUI. Дело в том, что text_entry - вражеский засланец, созданный не внутри метода setUI, а созданный внутри функции main, и прикрепленный к объекту root:

text_entry = Entry(textvariable=text)

(если родитель не указан, то используется корневое окно)

Объект Paint также прикреплен к root, но размещается при помощи pack, что и вызывает конфликт.

В данном случае нужно или перетащить создание text_entry внутрь метода setUI с указанием родителя self, или перетащить его размещение в main, и соответственно размещать через pack, а не grid. Также и размещение объекта Paint стоит выполнять не в самом Paint а в создающем его коде.

Еще у вас в коде есть глобальная проблема: использование global. Если вы уже доросли до ООП, то global вам не нужно.

→ Ссылка