TKinter – Column width = 0 leaves yellow pixels

Question:

I am trying to only show certain columns when pressing one of the buttons on the right side, for that reason I implemented to hide the other columns by reducing their column width to 0. While this works for hiding the columns, it leaves yellow pixels in their place which just doesn’t look right, see the following screenshots for reference.

Does anyone know how I can remove these pixels? (marked in yellow)

Screenshots:

Start view
Start

Tagesbasis view
Tagesbasis Button

Wochenbasis view
Wochenbasis

Code explanation:

Display Data Sheet Window:
    The data sheet window opens when the user clicks a button (e.g., "Show Data Sheet").
    It uses pd.read_excel to load an Excel file into a Pandas DataFrame (df).

Window Configuration:
    The window is configured to enable an interactive display of the styled DataFrame.
    A ttk.Treeview widget is created to represent the columns of the DataFrame.
    Column headers and widths are set.
    A scrollbar is added to navigate through the Treeview.

Search Functionality:
    A search field (search_entry) and a search button are added to search for specific data.
    The search_data function is called when the user presses the Enter key or clicks the search button.

Buttons for Different Views:
    Various buttons (e.g., "Daily Basis," "Order Basis," "Weekly Basis") are added to activate specific views.
    The corresponding functions (show_tagesbasis, show_bestellbasis, show_wochenbasis) are called to hide columns and update the view.

Reset Button:
    A "Reset" button is added to reset the view to its initial state.
    The reset_view function is called to show all columns again and update the view.

Color Coding (commented out):
    Originally, there was color coding for cells, but it has been commented out.

Relevant code:

# Import necessary libraries and modules
import tkinter as tk
from tkinter import ttk  # Import themed tk module
from tkcalendar import DateEntry  # Calendar functions
from babel import numbers
import datetime  # Date conversion
import matplotlib
matplotlib.use("TkAgg")  # Use Tkinter backend for plots
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
import matplotlib.pyplot as plt  # Graph functions
import numpy as np  # Graph functions
import matplotlib.dates as mdates  # Date conversion to weekdays
import locale  # X-axis graph labeling in German
from PIL import Image, ImageTk  # Image functions
import pandas as pd  # Import pandas for reading Excel data
from tkinter.ttk import Separator
import mplcursors  # Mouseover over chart points
import warnings  # Filter chart mouseover warnings: "missing pick support for PolyCollection and that the Selection.target.index attribute is deprecated."
import time  # For startup screen progress bar
import threading  # For startup screen progress bar
from functools import partial  # To link the progress bar with loading the DataFrame
from tkinter import messagebox  # For error message (e.g., empty Excel data entry)
import os

def on_datenblatt_button_click():
    print("Function started")

    try:
        # Load the Excel file
        file_path = 'data/Input.xlsx'
        df = pd.read_excel(file_path)

        # Check if the window is already open
        if hasattr(on_datenblatt_button_click, 'styled_window') and on_datenblatt_button_click.styled_window.winfo_exists():
            # If yes, close the window
            on_datenblatt_button_click.styled_window.destroy()
            return  # Exit the function to prevent reopening the window

        # Round the numbers to 2 decimal places
        df = df.round(2)

        # Create a new window for the interactive display of the styled DataFrame
        styled_window = tk.Toplevel(start_window)
        styled_window.title("Data Sheet")

        # Save the window as an attribute of the function to access it later
        on_datenblatt_button_click.styled_window = styled_window

        # Window size configuration (centered on the screen)
        window_width = 1000
        window_height = 600
        styled_window.geometry(f'{window_width}x{window_height}')
        # screen_width = styled_window.winfo_screenwidth()
        # screen_height = styled_window.winfo_screenheight()
        # x_coordinate = int((screen_width - window_width) / 2)
        # y_coordinate = int((screen_height - window_height) / 2)
        # styled_window.geometry(f'{window_width}x{window_height}+{x_coordinate}+{y_coordinate}')

        # Create a ttk.Treeview widget with a style
        style = ttk.Style()
        style.configure("Treeview", font=('Arial', 12))

        tree = ttk.Treeview(styled_window)
        tree["columns"] = list(df.columns)
        tree["show"] = "headings"  # Do not show an empty column on the left

        # Add column headers
        col_width = 120
        # Set the width for all columns
        for col in df.columns:
            tree.heading(col, text=col)
            tree.column(col, width=col_width, stretch=tk.NO)  # Set stretch to tk.NO and width to col_width

        # Pack the Treeview widget
        tree.pack(fill='both', side='left')

        # Add scrollbar
        scrollbar = ttk.Scrollbar(styled_window, orient='vertical', command=tree.yview)
        scrollbar.pack(side='right', fill='y')
        tree.configure(yscrollcommand=scrollbar.set)

        # Create a frame for the search
        search_frame = ttk.Frame(styled_window)
        search_frame.pack(side='top', fill='x')

        # Entry for the search
        search_entry = ttk.Entry(search_frame)
        search_entry.grid(row=0, column=0, padx=5, pady=5)
        # Bind the Enter key for search
        search_entry.bind('<Return>', lambda event: search_data(tree, df, search_entry.get()))

        # Button for the search
        search_button = ttk.Button(search_frame, text="Search", command=lambda: search_data(tree, df, search_entry.get()))
        search_button.grid(row=1, column=0, padx=5, pady=5)

        # An empty frame for spacing
        ttk.Frame(search_frame, height=10).grid(row=2, column=0)

        # Buttons for different views
        tagesbasis_button = ttk.Button(search_frame, text="Daily Basis", command=lambda: show_tagesbasis(tree, df, styled_window))
        tagesbasis_button.grid(row=3, column=0, padx=5, pady=5)

        bestellbasis_button = ttk.Button(search_frame, text="Order Basis", command=lambda: show_bestellbasis(tree, df, styled_window))
        bestellbasis_button.grid(row=4, column=0, padx=5, pady=5)

        wochenbasis_button = ttk.Button(search_frame, text="Weekly Basis", command=lambda: show_wochenbasis(tree, df, styled_window))
        wochenbasis_button.grid(row=5, column=0, padx=5, pady=5)

        # An empty frame for spacing
        ttk.Frame(search_frame, height=10).grid(row=6, column=0)

        reset_button = ttk.Button(search_frame, text="Reset", command=lambda: reset_view(tree, df))
        reset_button.grid(row=7, column=0, padx=5, pady=5)

        # # Set colors for cells
        # for tag in ['green', 'yellow', 'red']:
        #     tree.tag_configure(tag, background='')

        # Add entries without coloring
        for index, row in df.iterrows():
            values = list(row)
            tree.insert("", tk.END, values=values, iid=index)

            # Save the selection of the latest date and index
            if index == df.index[-1]:
                styled_window.selected_date = row['Date']
                styled_window.selected_index = index

        # Set selection to the latest date
        tree.selection_set(styled_window.selected_index)
        tree.see(styled_window.selected_index)

    except Exception as e:
        print(f"Error occurred: {e}")

    print("Function ended")

def search_data(tree, df, search_date):
    try:
        # Convert the date to a string and then search for it
        matching_rows = df.index[df['Date'].astype(str).str.contains(search_date, case=False, na=False)].tolist()
        if matching_rows:
            tree.selection_set(matching_rows[0])
            tree.see(matching_rows[0])
    except Exception as e:
        print(f"Error during search: {e}")

# def color_by_deviation(prognose_value, ist_value):
#     color = 'green' if ist_value == 0 or prognose_value == 0 else (
#         'green' if abs(ist_value - prognose_value) / ist_value <= 0.1 else (
#             'yellow' if abs(ist_value - prognose_value) / ist_value <= 0.3 else 'red'
#         )
#     )
#     return color

def show_tagesbasis(tree, df, styled_window):
    reset_view(tree, df)
    hide_column(tree, df, styled_window, '(Actual) Order', 'Forecast Order', '(Actual) Week', 'Forecast Week')

def show_bestellbasis(tree, df, styled_window):
    reset_view(tree, df)
    hide_column(tree, df, styled_window, '(Actual) Sales', 'Forecast Sales', '(Actual) Week', 'Forecast Week')

def show_wochenbasis(tree, df, styled_window):
    reset_view(tree, df)
    hide_column(tree, df, styled_window, '(Actual) Sales', 'Forecast Sales', '(Actual) Order', 'Forecast Order')

def reset_view(tree, df):
    show_all_columns(tree, df)
    # reset_colors(tree)
    update_entries(tree, df)

def hide_column(tree, df, styled_window, *columns_to_hide):
    for col in columns_to_hide:
        # Hide the column
        tree.column(col, width=0, stretch=tk.NO)

    # Update the Treeview widget
    styled_window.update_idletasks()

def update_entries(tree, df):
    for index, row in df.iterrows():
        values = list(row)
        tree.item(index, values=values)

def show_all_columns(tree, df):
    for col in df.columns:
        tree.column(col, width=120, stretch=tk.NO)
Asked By: Sld335

||

Answers:

There is a displaycolumns option of Treeview widget to select which columns to be shown.

Below is an example:

import tkinter as tk
from tkinter import ttk
import pandas as pd

root = tk.Tk()
root.minsize(1000, 300)

style = ttk.Style()
style.theme_use("default")
style.configure("Treeview", font=("Arial", 12))

columns = [f"col#{i}" for i in range(1, 8)]

tree = ttk.Treeview(root, columns=columns, show="headings")
tree.pack(side="left", fill="both", expand=1)

for col in columns:
    tree.heading(col, text=col)
    tree.column(col, width=120, stretch=0)

frame = tk.Frame(root)
frame.pack(side="right", fill="y")

# checkboxes for selecting columns to be shown
variables = {}
for col in columns:
    var = tk.IntVar(value=1)
    tk.Checkbutton(frame, text=col, variable=var).pack(fill="x", anchor="w")
    variables[col] = var

# function to show selected columns
def update_tree():
    show = [col for col,var in variables.items() if var.get()]
    tree.configure(displaycolumns=show)

tk.Button(frame, text="Update", command=update_tree).pack()

# sample dataframe
df = pd.DataFrame([[j*10+i for i in range(1, len(columns)+1)] for j in range(1, 10)])

# insert data into treeview
for index, row in df.iterrows():
    values = list(row)
    tree.insert("", "end", values=values, iid=index)

root.mainloop()

All columns shown:

enter image description here

Some columns hidden:

enter image description here

Answered By: acw1668