Исправь код таким образом, чтобы таблица с данными в основном окне приложения полностью помещалась в окно приложения. Сделай также так, чтобы при загрузке приложения оно разворачивалось на весь экран.
import tkinter as tk
from tkinter import ttk, filedialog, messagebox
import pandas as pd
import re
from functools import partial

def extract_price(text):
    if not isinstance(text, str):
        return ""
    m = re.search(r'(\d{1,3}(?:\s?\d{3})*)\s*руб\.', str(text).replace('\xa0', ' '))
    return m.group(1).replace(' ', '') if m else ""

def process_row(clean_lines, checked_top, checked_bottom):
    k_top = min(sum(checked_top), len(clean_lines))
    k_bottom = min(sum(checked_bottom), max(0, len(clean_lines) - k_top))
    name = " ".join(clean_lines[:k_top]) if k_top > 0 else ""
    desc_lines = clean_lines[k_top:len(clean_lines)-k_bottom if k_bottom > 0 else None]
    desc = "\n".join(desc_lines)
    return name, desc

def clean_lines_from_text(desc_raw):
    lines = [re.sub(r'\s+', ' ', l.strip().replace('\xa0', ' ')) for l in str(desc_raw).splitlines() if l.strip()]
    return lines

def calculate_price_excl_vat(price_str):
    try:
        price_float = float(price_str.replace(' ', '').replace(',', '.'))
        price_wo_vat = round(price_float / 1.2, 2)
        return str(price_wo_vat).replace('.', ',')
    except Exception:
        return ""

class TableWithCheckboxes(tk.Frame):
    def __init__(self, master, headers, *args, **kwargs):
        super().__init__(master, *args, **kwargs)
        self.headers = headers
        self.rows_widgets = []

        self.col_widths = [
            max(len(h), 16) if idx < 4 else max(len(h), 22)
            for idx, h in enumerate(headers)
        ]

        self.grid_rowconfigure(0, weight=1)
        self.grid_columnconfigure(0, weight=1)

        self.canvas = tk.Canvas(self, borderwidth=0, highlightthickness=0)
        self.v_scroll = ttk.Scrollbar(self, orient="vertical", command=self.canvas.yview)
        self.h_scroll = ttk.Scrollbar(self, orient="horizontal", command=self.canvas.xview)
        self.canvas.configure(yscrollcommand=self.v_scroll.set, xscrollcommand=self.h_scroll.set)

        self.canvas.grid(row=0, column=0, sticky='nsew')
        self.v_scroll.grid(row=0, column=1, sticky='ns')
        self.h_scroll.grid(row=1, column=0, sticky='ew')

        self.table_frame = tk.Frame(self.canvas)
        self.canvas_frame_id = self.canvas.create_window((0, 0), window=self.table_frame, anchor="nw")
        self.table_frame.bind("<Configure>", self._on_frame_configure)
        self.canvas.bind("<Configure>", self._on_canvas_configure)

        self.header_labels = []
        self._make_header()

        for i, width in enumerate(self.col_widths):
            self.table_frame.grid_columnconfigure(i, weight=1, minsize=width * 8)

    def _make_header(self):
        for lbl in self.header_labels:
            lbl.destroy()
        self.header_labels.clear()
        for i, hdr in enumerate(self.headers):
            wrap_len_px = self.col_widths[i] * 7
            lbl = tk.Label(self.table_frame, text=hdr, relief="ridge", borderwidth=2,
                           font=('Arial', 11, 'bold'), width=self.col_widths[i], height=2,
                           anchor="center", bg="#dde7f3", wraplength=wrap_len_px, justify='center')
            lbl.grid(row=0, column=i, sticky="nsew", padx=2, pady=2)
            self.header_labels.append(lbl)
            self.table_frame.grid_columnconfigure(i, weight=1, minsize=self.col_widths[i] * 8)

    def _on_frame_configure(self, event):
        self.canvas.configure(scrollregion=self.canvas.bbox("all"))

    def _on_canvas_configure(self, event):
        total_content_width = sum(w * 8 for w in self.col_widths) + 50
        self.canvas.itemconfigure(self.canvas_frame_id, width=total_content_width)

    def clear_table(self):
        for widgets in self.rows_widgets:
            for w in widgets:
                w.destroy()
        self.rows_widgets.clear()
        self.canvas.yview_moveto(0)
        self.canvas.xview_moveto(0)

    def update_column_widths(self, texts, col_idx):
        current_max_len = self.col_widths[col_idx]
        max_text_len = max((len(str(t)) for t in texts), default=0)
        header_len = len(self.headers[col_idx])

        if col_idx == len(self.headers) - 1 or col_idx == len(self.headers) - 2:
            base_min_width = 22
        else:
            base_min_width = 16

        new_width = max(current_max_len, max_text_len, header_len, base_min_width)

        if new_width > self.col_widths[col_idx]:
            self.col_widths[col_idx] = new_width
            self._make_header()
            for row_widgets in self.rows_widgets:
                if row_widgets and len(row_widgets) > col_idx:
                    if col_idx <= 4:
                        row_widgets[col_idx].config(width=self.col_widths[col_idx])
                    elif col_idx >= 5:
                        row_widgets[col_idx].config(width=self.col_widths[col_idx] * 8)

            self._on_canvas_configure(
                self.canvas.winfo_exists() and
                self.canvas.winfo_width() > 0 and
                self.canvas.winfo_height() > 0 and
                self.canvas or None)

    def insert_row(self, row_idx, art, name, descr, price, price_wo_vat, vars_top, vars_bottom, callback):
        row = []

        desc_lines = descr.split('\n')
        n_lines = max(1, len(desc_lines))
        font = ('Arial', 10)
        bg = "#f9fafd" if row_idx % 2 == 0 else "#f2f6fb"

        art_lbl = tk.Label(self.table_frame, text=art, relief='ridge', borderwidth=1,
                           width=self.col_widths[0], height=n_lines, font=font, anchor="w", justify='left', bg=bg)
        art_lbl.grid(row=row_idx + 1, column=0, sticky="nsew")
        row.append(art_lbl)

        name_lbl = tk.Label(self.table_frame, text=name, relief='ridge', borderwidth=1,
                            wraplength=self.col_widths[1]*8, height=n_lines, font=font,
                            anchor="w", justify='left', bg=bg)
        name_lbl.grid(row=row_idx + 1, column=1, sticky="nsew")
        row.append(name_lbl)

        descr_lbl = tk.Label(self.table_frame, text=descr, relief='ridge', borderwidth=1,
                             width=self.col_widths[2], height=n_lines, font=font, anchor="w", justify='left', bg=bg)
        descr_lbl.grid(row=row_idx + 1, column=2, sticky="nsew")
        row.append(descr_lbl)

        price_lbl = tk.Label(self.table_frame, text=price, relief='ridge', borderwidth=1,
                             width=self.col_widths[3], height=n_lines, font=font, anchor='center', justify='center', bg=bg)
        price_lbl.grid(row=row_idx + 1, column=3, sticky="nsew")
        row.append(price_lbl)

        price_wo_vat_lbl = tk.Label(self.table_frame, text=price_wo_vat, relief='ridge', borderwidth=1,
                             width=self.col_widths[4], height=n_lines, font=font, anchor='center', justify='center', bg=bg)
        price_wo_vat_lbl.grid(row=row_idx + 1, column=4, sticky="nsew")
        row.append(price_wo_vat_lbl)

        # Фреймы с чекбоксами в колонках 5 и 6
        cb_frame_top = tk.Frame(self.table_frame, relief='ridge', borderwidth=1, bg=bg)
        cb_frame_top.grid(row=row_idx + 1, column=5, sticky="nsew", padx=2, pady=2)
        row.append(cb_frame_top)
        for i in range(3):
            cb = tk.Checkbutton(cb_frame_top, variable=vars_top[i], text=str(i + 1),
                                command=callback, bg=bg)
            cb.pack(side='left', padx=5, pady=n_lines * 6 // 3)

        cb_frame_bottom = tk.Frame(self.table_frame, relief='ridge', borderwidth=1, bg=bg)
        cb_frame_bottom.grid(row=row_idx + 1, column=6, sticky="nsew", padx=2, pady=2)
        row.append(cb_frame_bottom)
        for i in range(3):
            cb = tk.Checkbutton(cb_frame_bottom, variable=vars_bottom[i], text=str(i + 1),
                                command=callback, bg=bg)
            cb.pack(side='left', padx=5, pady=n_lines * 6 // 3)

        self.rows_widgets.append(row)

class DynamicTableApp:
    def __init__(self, master):
        self.master = master
        self.master.title("Milesight: Таблица с динамическими чекбоксами")
        self.master.geometry("1650x900")  # Увеличенная ширина окна

        self.df = None
        self.rowdata = []

        self.headers = [
            'Артикул', 'Наименование', 'Описание', 'РРЦ', 'РРЦ (без НДС)',
            'Удалить первые строки', 'Удалить последние строки'
        ]

        top_frame = ttk.Frame(master)
        top_frame.pack(fill='x', pady=5)

        ttk.Button(top_frame, text="Загрузить Excel", command=self.load_file).pack(side='left', padx=10)

        # Добавлено поле для ввода текста фильтрации
        filter_frame = ttk.Frame(master)
        filter_frame.pack(fill='x', pady=5)
        ttk.Label(filter_frame, text="Текст для исключения строк (фильтр):").pack(side='left', padx=5)
        self.filter_text_var = tk.StringVar(value="Модель снята с производства")
        filter_entry = ttk.Entry(filter_frame, textvariable=self.filter_text_var, width=60)
        filter_entry.pack(side='left', padx=5, fill='x', expand=True)

        sel_frame = ttk.Frame(master)
        sel_frame.pack(fill='x', pady=5)

        ttk.Label(sel_frame, text="Артикул:").grid(row=0, column=0, sticky='e')
        self.art_var = tk.StringVar()
        self.art_cb = ttk.Combobox(sel_frame, textvariable=self.art_var, width=30)
        self.art_cb.grid(row=0, column=1, padx=5)

        ttk.Label(sel_frame, text="Описание (Text3):").grid(row=0, column=2, sticky='e')
        self.desc_var = tk.StringVar()
        self.desc_cb = ttk.Combobox(sel_frame, textvariable=self.desc_var, width=30)
        self.desc_cb.grid(row=0, column=3, padx=5)

        ttk.Button(sel_frame, text="Показать таблицу", command=self.process).grid(row=0, column=4, padx=10)

        self.save_btn = ttk.Button(sel_frame, text="Сохранить", command=self.save, state='disabled')
        self.save_btn.grid(row=0, column=5)

        self.table = TableWithCheckboxes(master, self.headers)
        self.table.pack(fill='both', expand=True, padx=10, pady=10)

    def load_file(self):
        filename = filedialog.askopenfilename(filetypes=[("Excel files", "*.xlsx"), ("All files", "*.*")])
        if not filename:
            return
        self.df = pd.read_excel(filename)
        cols = list(self.df.columns)
        self.art_cb['values'] = cols
        self.desc_cb['values'] = cols
        self.art_var.set('Text2' if 'Text2' in cols else (cols[0] if cols else ''))
        self.desc_var.set('Text3' if 'Text3' in cols else (cols[-1] if cols else ''))
        messagebox.showinfo("Готово", "Файл успешно загружен")

    def process(self):
        if self.df is None:
            messagebox.showerror("Ошибка", "Сначала загрузите файл")
            return

        filter_text = self.filter_text_var.get().strip()
        self.table.clear_table()
        self.table.col_widths = [
            max(len(h), 16) if idx < len(self.headers) - 2 else max(len(h), 22)
            for idx, h in enumerate(self.headers)
        ]
        self.table._make_header()
        self.rowdata.clear()

        art_col = self.art_var.get()
        desc_col = self.desc_var.get()

        idx_for_rowdata = 0  # Переменная для индексирования элементов rowdata и таблицы после фильтрации

        for i, row in enumerate(self.df.itertuples()):
            desc_raw = getattr(row, desc_col)
            # Пропускаем строки, если в описании есть фильтрующий текст
            if filter_text and isinstance(desc_raw, str) and filter_text in desc_raw:
                continue

            art = getattr(row, art_col) if hasattr(row, art_col) else ""
            price_str = extract_price(desc_raw)
            price_wo_vat_str = calculate_price_excl_vat(price_str)
            lines = clean_lines_from_text(desc_raw)

            vars_top = [tk.IntVar(value=1 if j < 2 else 0) for j in range(3)]
            vars_bottom = [tk.IntVar(value=1) for _ in range(3)]

            callback = partial(self.update_row, idx_for_rowdata)
            for v in vars_top + vars_bottom:
                v.trace_add('write', callback)

            name, descr = process_row(lines, [v.get() for v in vars_top], [v.get() for v in vars_bottom])

            self.table.update_column_widths([art], 0)
            self.table.update_column_widths([name], 1)
            self.table.update_column_widths(lines, 2)
            self.table.update_column_widths([price_str], 3)
            self.table.update_column_widths([price_wo_vat_str], 4)

            self.table.insert_row(idx_for_rowdata, art, name, descr, price_str, price_wo_vat_str,
                                  vars_top, vars_bottom, callback)

            self.rowdata.append({
                'lines': lines,
                'art': art,
                'desc_raw': desc_raw,
                'price_str': price_str,
                'price_wo_vat_str': price_wo_vat_str,
                'vars_top': vars_top,
                'vars_bottom': vars_bottom,
            })

            idx_for_rowdata += 1

        self.save_btn.config(state='normal')
        self.table.canvas.update_idletasks()
        self.table.canvas.config(scrollregion=self.table.canvas.bbox("all"))

    def update_row(self, idx, *args):
        item = self.rowdata[idx]
        lines = item['lines']
        top_state = [v.get() for v in item['vars_top']]
        bottom_state = [v.get() for v in item['vars_bottom']]
        name, descr = process_row(lines, top_state, bottom_state)

        art_lbl, name_lbl, desc_lbl, price_lbl, price_wo_vat_lbl, cbf_top, cbf_bottom = self.table.rows_widgets[idx]

        name_lbl.config(text=name)

        k_top = min(sum(top_state), len(lines))
        k_bottom = min(sum(bottom_state), max(0, len(lines) - k_top))
        descr_lines = lines[k_top:len(lines) - k_bottom if k_bottom > 0 else None]
        new_text = '\n'.join(descr_lines)
        desc_lbl.config(text=new_text)

        n_lines_desc = new_text.count('\n') + 1 if new_text else 1

        desc_lbl.config(height=n_lines_desc)
        art_lbl.config(height=n_lines_desc)
        price_lbl.config(height=n_lines_desc)
        price_wo_vat_lbl.config(height=n_lines_desc)

        pady_val = max(4, n_lines_desc * 6 // 3)
        for cb in cbf_top.winfo_children():
            cb.pack_configure(pady=pady_val)
        for cb in cbf_bottom.winfo_children():
            cb.pack_configure(pady=pady_val)

    def save(self):
        result = {
            'Артикул': [],
            'Наименование': [],
            'Описание': [],
            'РРЦ': [],
            'РРЦ (без НДС)': [],
        }
        for i, item in enumerate(self.rowdata):
            lines = item['lines']
            top_state = [v.get() for v in item['vars_top']]
            bottom_state = [v.get() for v in item['vars_bottom']]
            name, desc = process_row(lines, top_state, bottom_state)
            result['Артикул'].append(item['art'])
            result['Наименование'].append(name)
            result['Описание'].append(desc)
            result['РРЦ'].append(item['price_str'])
            result['РРЦ (без НДС)'].append(item['price_wo_vat_str'])
        dfout = pd.DataFrame(result)
        fname = filedialog.asksaveasfilename(defaultextension=".xlsx", filetypes=[("Excel files", "*.xlsx")])
        if not fname:
            return
        dfout.to_excel(fname, index=False)
        messagebox.showinfo("Готово", "Файл успешно сохранён")

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