You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Tkinter Grid布局中columnspan破坏列均匀权重问题咨询

Tkinter Grid布局中columnspan与weight、uniform结合的异常问题

在使用CustomTkinter的Grid布局时,遇到columnspan属性与weight、uniform配置结合后的布局异常:当为两个表格组件设置columnspan=2时,它们会占用超出预期的空间,直接挤占了其他配置了uniform权重的列。按Grid布局的设计逻辑,在定义了uniform分组和weight权重后,网格单元格的大小应该由配置的规则决定,子组件不应强制修改,但设置columnspan后却出现了违背该逻辑的问题。

主类代码

import customtkinter as ctk
import sql
import pandas as pd
from gui_elements import *

class Menu_Window(ctk.CTk):
    def __init__(self, width = 1280, height = 720, version = '0.0.1'):
        super().__init__()
        self.geometry(f"{width}x{height}")

        self.protocol("WM_DELETE_WINDOW", self.destroy)

        self.rowconfigure((0, 7), weight=1, uniform='r')
        self.rowconfigure((1,2,3,4,6), weight=2, uniform='r')
        self.rowconfigure(5, weight=12, uniform='r')

        self.columnconfigure((0, 3, 6), weight=3, uniform='c')
        self.columnconfigure((1,2,4,5), weight=3, uniform='c')



        df_display = pd.DataFrame()
        df_display['Index'] = [0, 1, 2, 3, 4, 5, 6]
        df_display['Time'] = [0, 1, 2, 3, 4, 5, 6]
        df_display['A'] = [0, 1, 2, 3, 4, 5, 6]
        df_display['B'] = [0, 1, 2, 3, 4, 5, 6]
        df_display['C'] = [0, 1, 2, 3, 4, 5, 6]
        df_display['D'] = [0, 1, 2, 3, 4, 5, 6]

        #table = ctk.CTkFrame(self, fg_color='red')
        table = CTkTable(self, dataframe=df_display)
        table.grid(row=5, column=1, columnspan=2 ,sticky='nsew')

        #table2 = ctk.CTkFrame(self, fg_color='blue')
        table2 = CTkTable(self, dataframe=df_display)
        table2.grid(row=5, column=4, columnspan=2 ,sticky='nsew')



if __name__ == '__main__':
    menu_window = Menu_Window()
    menu_window.mainloop()

表格类代码

import random
import numpy as np
import math
import customtkinter as ctk

class CTkTable(ctk.CTkFrame):
    def __init__(self, master, dataframe=None, view_bounds = [8, 5]):
        super().__init__(master, corner_radius=0)
        self.font_header = ("Book Antiqua", 12)
        self.font_data = ("Book Antiqua", 12)
        self.view_bounds = view_bounds
        if self.view_bounds[1] < 3:
            self.view_bounds[1] = 3
        self.column_offset_max = -1
        self.row_offset_max = -1
        self.column_offset = 0
        self.row_offset = 0

        self.color = 'white'
        self.dataframe = dataframe
        self.headers = []
        self.table_frame_array = []
        self.frame_column_header_array = []

        self.frame_column_header = ctk.CTkFrame(master=self, corner_radius=0)
        self.frame_column_time_header = ctk.CTkFrame(master=self, corner_radius=0)
        self.frame_column_index_header = ctk.CTkFrame(master=self, corner_radius=0)

        self.frame_table = ctk.CTkFrame(master=self, corner_radius=0)
        self.v_scroll = ctk.CTkSlider(master=self, orientation='vertical', command=lambda value: self.update_view(1-value, dir='row'), fg_color = self.color)
        self.v_scroll.set(1)
        self.h_scroll = ctk.CTkSlider(master=self, orientation='horizontal', command=lambda value: self.update_view(value, dir='col'), fg_color = self.color)
        self.h_scroll.set(0)
        self.button_settings = ctk.CTkButton(master=self, text='⚙', font=self.font_header, text_color='black', fg_color='gray', corner_radius=0)

        if self.dataframe is not None:
            self.selected_cols = self.dataframe.columns.tolist()
            self.selected_cols.remove('Time')
            self.selected_cols.remove('Index')
            self.setup_table_data()

    def setup_table_data(self):
        self.table_frame_array = []
        self.frame_column_header_array = []
        num_rows = min(len(self.dataframe[self.selected_cols[-1]]), self.view_bounds[0])
        num_columns = min(len(self.selected_cols), self.view_bounds[1])-2

        self.rowconfigure(0, weight = 2, uniform='r')
        self.rowconfigure(1, weight = 16, uniform='r')
        self.rowconfigure(2, weight = 1, uniform='r')


        z = lcm(num_columns+2, 19)
        x = z * (num_columns+2) / 19
        self.columnconfigure(0, weight = int(z), uniform='c')
        self.columnconfigure(1, weight = int(z), uniform='c')
        self.columnconfigure(2, weight = int(z*num_columns), uniform='c')
        self.columnconfigure(3, weight = int(x), uniform='c')

        self.frame_column_header.grid(row=0, column=2, sticky='nsew')
        self.frame_table.grid(row=1, column=2, sticky='nsew')
        self.v_scroll.grid(row=1, column=3, sticky='ns')
        self.h_scroll.grid(row=2, column=2, sticky='ew')
        self.button_settings.grid(row=0, column=3, sticky='nsew')

        self.frame_column_time_header.grid(row=0, column=0, sticky='nsew')
        self.frame_column_time_header.rowconfigure(0, weight=1, uniform='r')
        self.frame_column_time_header.columnconfigure(0, weight=1, uniform='c')

        self.frame_column_index_header.grid(row=0, column=1, sticky='nsew')
        self.frame_column_index_header.rowconfigure(0, weight=1, uniform='r')
        self.frame_column_index_header.columnconfigure(0, weight=1, uniform='c')

        self.frame_column_header.rowconfigure(0, weight=1, uniform='r')
        self.frame_column_header.columnconfigure(tuple(x for x in range(num_columns)), weight=1, uniform='c')
        self.frame_table.rowconfigure(0, weight=1, uniform='r')
        self.frame_table.columnconfigure(tuple(x for x in range(num_columns)), weight=1, uniform='c')
        self.set_offset_range()

        self.label_time = ctk.CTkLabel(self.frame_column_time_header, text='Time', fg_color=self.color, font=self.font_header)
        self.label_time.grid(row=0, column=0, sticky='nsew', padx=2, pady=2)

        self.label_index = ctk.CTkLabel(self.frame_column_index_header, text='Index', fg_color=self.color, font=self.font_header)
        self.label_index.grid(row=0, column=0, sticky='nsew', padx=2, pady=2)

        self.frame_column_time = CTkTableColumn(self, 'Time', self.dataframe['Time'], self.row_offset, num_rows, self.color, self.font_data)
        self.frame_column_time.grid(row=1, column=0, sticky='nsew')

        self.frame_column_index = CTkTableColumn(self, 'Index', self.dataframe['Index'], self.row_offset, num_rows,self.color, self.font_data)
        self.frame_column_index.grid(row=1, column=1, sticky='nsew')


        for i in range(num_columns):
            # color = f"#{random.randint(0, 0xFFFFFF):06x}"
            frame = ctk.CTkFrame(self.frame_column_header, corner_radius=0)
            frame.grid(row=0, column=i, sticky='nsew')
            frame.rowconfigure(0, weight=1, uniform='r')
            frame.columnconfigure(0, weight=1, uniform='c')

            self.frame_column_header_array.append(ctk.CTkLabel(frame, text=self.selected_cols[i+self.column_offset], fg_color=self.color, font=self.font_header))
            self.frame_column_header_array[-1].grid(row=0, column=0, sticky='nsew', padx=2, pady=2)

            self.table_frame_array.append(CTkTableColumn(self.frame_table, self.selected_cols[i+self.column_offset],  self.dataframe[self.selected_cols[i+self.column_offset]], self.row_offset, num_rows, self.color, self.font_data))
            self.table_frame_array[-1].grid(row=0, column=i, sticky='nsew')

    def update_table(self):
        num_rows = min(len(self.dataframe[self.selected_cols[-1]]), self.view_bounds[0])
        num_columns = min(len(self.selected_cols), self.view_bounds[1])-2

        for table_column in [self.frame_column_time, self.frame_column_index]:
            data = self.dataframe[table_column.header]
            for j in range(num_rows):
                text = ''
                if j + self.row_offset < len(data):
                    if table_column.header == 'Index':
                        text = str(int(data[j + self.row_offset]))
                    else:
                        try:
                            text = str(signif(data[j + self.row_offset], 4))
                        except:
                            text = str(data[j + self.row_offset])
                table_column.array_labels[j].configure(text=text.replace(' ', '\n'))


        for i in range(num_columns):
            self.frame_column_header_array[i].configure(text = self.selected_cols[i+self.column_offset])
            data = self.dataframe[self.selected_cols[i+self.column_offset]]
            for j in range(num_rows):
                text = ''
                if j + self.row_offset < len(data):
                    if self.table_frame_array[i].header == 'Index':
                        text = int(data[j + self.row_offset])
                    else:
                        try:
                            text = str(signif(data[j + self.row_offset], 4))
                        except:
                            text = str(data[i + self.row_offset])
                self.table_frame_array[i].array_labels[j].configure(text=text.replace(' ', '\n'))

    def update_view(self, value, dir):
        row_offset = round(value*self.row_offset_max)
        column_offset = self.column_offset
        if dir == 'col':
            column_offset = round(value*self.column_offset_max)
            row_offset = self.row_offset

        if row_offset != self.row_offset or column_offset!= self.column_offset:
            self.row_offset = row_offset
            self.column_offset = column_offset
            self.update_table()

    def set_offset_range(self):
        self.row_offset_max = len(self.dataframe[self.selected_cols[0]]) - self.view_bounds[0]
        self.column_offset_max = len(self.selected_cols) - self.view_bounds[1] + 2



class CTkTableColumn(ctk.CTkFrame):
    def __init__(self, master, header, data, offset, num_rows, color, font):
        super().__init__(master, corner_radius=0)
        self.header = header
        self.array_frames = []
        self.array_labels = []

        self.rowconfigure(tuple(x for x in range(num_rows)), weight=1, uniform='r')
        self.columnconfigure(0, weight=1, uniform='c')

        for i in range(num_rows):
            text = ''
            if i+offset < len(data):
                if header == 'Index':
                    text = str(int(data[i+offset]))
                else:
                    try:
                        text = str(signif(data[i+offset], 4))
                    except:
                        text = str(data[i+offset])
            self.array_frames.append(ctk.CTkFrame(master=self, corner_radius=0))
            self.array_frames[-1].grid(row=i, column=0, sticky='nsew')
            self.array_frames[-1].rowconfigure(0, weight=1, uniform='r')
            self.array_frames[-1].columnconfigure(0, weight=1, uniform='c')

            self.array_labels.append(ctk.CTkLabel(master=self.array_frames[-1], text=text.replace(' ', '\n'), anchor='center', fg_color=color, font=font))
            self.array_labels[-1].grid(row=0, column=0, sticky='nsew', padx=2, pady=2)

def signif(arr, p):
    flag = False
    if isinstance(arr, np.ndarray):
        arr = arr.tolist()
    if not isinstance(arr, list):
        arr = [arr]
        flag = True

    arr_out = []
    for x in arr:
        x_positive = np.where(np.isfinite(x) & (x != 0), np.abs(x), 10**(p-1))
        mags = 10 ** (p - 1 - np.floor(np.log10(x_positive)))
        x = np.round(x * mags) / mags
        arr_out.append(np.format_float_positional(x, precision=p, unique=False, fractional=False, trim='k'))
    if flag:
        return arr_out[0]
    else:
        return np.asarray(arr_out)


def lcm(a, b):
    return abs(a * b) // math.gcd(a, b)

效果展示

预期效果

预期效果

两个表格均设置columnspan=2时的效果

两个表格均设columnspan=2时的效果

表格1设columnspan=2、表格2设columnspan=1时的效果

表格1设columnspan=2、表格2设columnspan=1时的效果

内容的提问来源于stack exchange,提问作者Dak

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.01 11:54:50