首页 > 解决方案 > tkinter 框架不随画布展开

问题描述

我正在尝试为时间序列数据创建一个 GUI。它有 2 个可滚动的画布和一个框架。顶部框架包含其他框架。main_frame 似乎随着画布扩展,但 catmainframes 似乎没有这样做。catmainframe 用于动态生成帧。

import os
import tkinter as tk
#from tkinter import ttk
from tkinter import filedialog as fd
import pandas as pd
import matplotlib.pyplot as plt
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg

def on_configure(event):
    # update scrollregion after starting 'mainloop'
    canvas1.configure(scrollregion=canvas1.bbox('all'))
    canvas2.configure(scrollregion=canvas2.bbox('all'))

def enterCategory():
    global cat
    global catmainframe
    global rownum
    global add_file
    cat = category.get()
    catmainframe = tk.Frame(main_frame, borderwidth=2, relief="solid")
    catmainframe.grid(row=rownum,column=0,sticky='nsew', padx=3, pady=3)
    #canvas1.create_window((0,0), window=catmainframe,anchor='nw')

    catmainframe.grid_rowconfigure(rownum, weight=1)
    catmainframe.grid_columnconfigure(0, weight=1)     
    catframe = tk.Frame(catmainframe, borderwidth=2, relief="solid")
    catframe.grid(row=0,column=0, sticky='nsew', padx=1, pady=1)

    catlabel = tk.Label(catframe, text=cat)
    catlabel.grid(row=0, column=0, sticky='nsew')

    add_file = tk.Button(catframe,text="Add File",command=openFile)
    add_file.grid(row=0, column=1, sticky='nsew')

    global catchildframe
    catchildframe = tk.Frame(catmainframe, borderwidth=2, relief="solid")
    catchildframe.grid(row=1,column=0,sticky='nsew', padx=1, pady=1)
    catchildframe.grid_rowconfigure(1, weight=1)
    catchildframe.grid_columnconfigure(1, weight=1)  
    global box1, box2, box3
    box1 = tk.Frame(catchildframe, borderwidth=2, relief="solid")
    box2 = tk.Frame(catchildframe, borderwidth=2, relief="solid")
    box3 = tk.Frame(catchildframe, borderwidth=2, relief="solid")
    box1.grid(row=1, column=0, sticky='nsew', padx=10, pady=10)
    box2.grid(row=1, column=1, sticky='nsew', padx=10, pady=10)
    box3.grid(row=1, column=2, sticky='nsew', padx=10, pady=10)
    box1.propagate(1)
    box2.propagate(1)
    box3.propagate(1)

    rownum =  rownum +1

def openFile():
    global fname
    global mindatetime
    global maxdatetime
    parentname = catmainframe.winfo_parent()
    parent = catmainframe._nametowidget(parentname)

    #childname = catchildframe.winfo_parent()
    #child = catchildframe._nametowidget(childname)
    child = add_file.master

    print("Catmainframe parent:"+parentname)
    #print("Catchildframe parent:"+child)

    file_path=fd.askopenfilename()
    #print(file_path)

    file_name = os.path.basename(file_path)
    print(file_name)

    file_list = []

    file_list.append(file_name)

    df = pd.read_csv(file_path)
    names = list(df.columns[0:])
    indexcol = names[0]
    #print(indexcol)
    df = df.rename(columns = {indexcol:'datetime'})
    names = list(df.columns[1:])
    #print(names)
    df.datetime = pd.to_datetime(df.datetime)
    df.set_index('datetime',inplace=True)

    if mindatetime == pd.to_datetime('1900-01-01 00:00:00'):
        mindatetime = df.index.min()
    elif mindatetime > df.index.min():
        mindatetime = df.index.min()

    if maxdatetime == pd.to_datetime('1900-01-01 00:00:00'):
        maxdatetime = df.index.max()
    elif maxdatetime < df.index.max():
        maxdatetime = df.index.max()

    print(mindatetime)
    print(maxdatetime)    
    global unique_dates
    unique_dates = []
    unique_dates = df.index.map(pd.Timestamp.date).unique()

    for x in range(len(names)):    
        if(len(names)==1):
            l = tk.Checkbutton(box1, text=names[x], variable=names[x],state='disabled')
            l.select()
            l.pack(anchor = 'w')
        else:
            l = tk.Checkbutton(box1, text=names[x], variable=names[x])
            l.select()
            l.pack(anchor = 'w')

    figure = plt.Figure(figsize=(4,3), dpi=100)
    ax2 = figure.add_subplot(111)
    line = FigureCanvasTkAgg(figure, box2)
    line.get_tk_widget().grid(row=1,column=1,sticky='nsew')
    df.plot(kind='line', legend=False, ax=ax2, fontsize=10)
    ax2.set_title(cat)
    ax2.set_xlim(mindatetime,maxdatetime)

    for x in range(len(unique_dates)):
        d = tk.Checkbutton(box3, text=unique_dates[x], variable=unique_dates[x])
        d.select()
        d.pack(anchor = 'w')

root = tk.Tk()
root.geometry('{}x{}'.format(800, 600))

# layout all of the main containers
root.grid_rowconfigure(1, weight=1)
root.grid_columnconfigure(0, weight=1)


#Global variables
category = tk.StringVar()
global rownum
rownum =0
mindatetime = pd.to_datetime('1900-01-01 00:00:00')
maxdatetime = pd.to_datetime('1900-01-01 00:00:00')

#Top frame
top_frame = tk.Frame(root)
top_frame.grid(row=0, column=0, sticky='nsew')

category_name = tk.Label(top_frame, text='Category:')
category_name.grid(row=0, column=0, sticky='nsew')

entry_category = tk.Entry(top_frame, background="pink",textvariable = category)
entry_category.grid(row=0, column=1, sticky='nsew')
entry_category.focus()

ok_button = tk.Button(top_frame, text="OK", command=enterCategory)
ok_button.grid(row=0, column=2, sticky='nsew')

xscrollbar = tk.Scrollbar(root, orient='horizontal')
xscrollbar.grid(row=3, column=0, sticky='ew')

yscrollbar = tk.Scrollbar(root)
yscrollbar.grid(row=1, column=1, sticky='ns')

canvas1 = tk.Canvas(root, bd=0,#scrollregion=(0, 0, 1000, 1000),
                yscrollcommand=yscrollbar.set)
canvas1.grid(row=1, column=0, sticky='nsew')

# create the center widgets
canvas1.grid_rowconfigure(1, weight=1)
canvas1.grid_columnconfigure(0, weight=1)

canvas2 = tk.Canvas(root, bd=0,#scrollregion=(0, 0, 1000, 1000),
                xscrollcommand=xscrollbar.set)
canvas2.grid(row=2, column=0, sticky='nsew')

xscrollbar.config(command=canvas2.xview)
yscrollbar.config(command=canvas1.yview)


canvas1.config(scrollregion=canvas1.bbox("all"))

canvas2.config(scrollregion=canvas2.bbox("all"))

main_frame = tk.Frame(canvas1)

canvas1.create_window((0,0), window=main_frame,anchor='nw')
#main_frame.grid(row=0,column=0,stick='nsew')

time_box = tk.Frame(canvas2)
canvas2.create_window((0,0), window=time_box,anchor='nw')

root.bind('<Configure>', on_configure) 
root.mainloop()

以下是错误,即使应用程序似乎运行良好。

Traceback (most recent call last):
  File "C:\Users\ranji\Anaconda3\lib\tkinter\__init__.py", line 1702, in __call__
    return self.func(*args)
  File "C:\Users\ranji\Anaconda3\lib\tkinter\__init__.py", line 3069, in set
    self.tk.call(self._w, 'set', first, last)
_tkinter.TclError: invalid command name ".!scrollbar2"
Exception in Tkinter callback
Traceback (most recent call last):
  File "C:\Users\ranji\Anaconda3\lib\tkinter\__init__.py", line 1702, in __call__
    return self.func(*args)
  File "C:\Users\ranji\Anaconda3\lib\tkinter\__init__.py", line 3069, in set
    self.tk.call(self._w, 'set', first, last)
_tkinter.TclError: invalid command name ".!scrollbar"

标签: python-3.xcanvastkinter

解决方案


您必须自己手动管理内框的大小。您可以通过在画布更改大小时将内部窗口的宽度显式设置为画布的宽度(减去任何所需的边框或边距)来做到这一点。<Configure>当窗口改变大小时,您可以通过绑定到事件来获得回调。

我已将您的代码缩减为所询问的问题。请注意,在下面的代码中,我向嵌入的窗口对象添加了一个标签,以便可以在on_configure函数中引用它。我还对内部框架进行了着色以使其更易于可视化,并给它一个高度,因为内部没有小部件。

这个例子的重要部分是对canvas1.itemconfigurein的调用on_configure

import tkinter as tk

def on_configure(event):
    width = canvas1.winfo_width()
    canvas1.itemconfigure("main_frame", width=width)

root = tk.Tk()
root.grid_rowconfigure(1, weight=1)
root.grid_columnconfigure(0, weight=1)

yscrollbar = tk.Scrollbar(root)
canvas1 = tk.Canvas(root, bd=0,yscrollcommand=yscrollbar.set)

canvas1.grid(row=1, column=0, sticky='nsew')
yscrollbar.grid(row=1, column=1, sticky='ns')

main_frame = tk.Frame(canvas1, background="bisque", height=200)
canvas1.create_window((0,0), window=main_frame,anchor='nw', tags=("main_frame",))

root.bind('<Configure>', on_configure)
root.mainloop()

推荐阅读