Canvas 中的滚动条不起作用 - Tkinter Python

Scrollbar in Canvas doesn't work - Tkinter Python

几天来,我一直在努力为我的 canvas 小部件添加一个工作滚动条。 我正在开发一个小应用程序来显示 excel 数据。我已经尝试了很多东西,但无法取得工作结果:/

我用“file_opener”函数填充 canvas2,我想将滚动条添加到 canvas2 的第 7 列。但是,在我之前的尝试中,滚动条只出现在第 0 行并且没有该功能。

我是一名自学初学者,非常感谢您的帮助。

这是它的样子:“https://ibb.co/W2d674g”

这是我的代码:

import tkinter
from tkinter import *
import pandas as pd

class App:
    def __init__(self, window):
        self.window = window

        window.title("Excel Magician")
        window.geometry("800x800")

        self.canvas1 = tkinter.Canvas(window, width = 720, height = 200)
        self.canvas1.grid(row=0, column=0)

        self.frame1 = tkinter.Frame(window, width = 720,height = 20, bg = '#0ca274')
        self.frame1.grid(row=1, column=0, pady=4)

        self.canvas2 = tkinter.Canvas(window, width = 720,height = 300, bg = '#0ca274')
        self.canvas2.grid(row=2, column=0, pady=1)

        self.column_list = ['CLIENT','Column2','Column3','Column4','Column5']
        for i in range(len(self.column_list)):
            tkinter.Label(self.frame1, text= self.column_list[i], font=('Bahnschrift',10)).grid(row= 0, column= i, sticky='e', ipadx=50)

        self.user_label = tkinter.Label(self.canvas1, text='USERNAME', font=('Bahnschrift',10))
        self.canvas1.create_window(600, 60, window=self.user_label)

        self.user_name = tkinter.Entry (self.canvas1) 
        self.canvas1.create_window(600, 80, window=self.user_name)

        self.client_label = tkinter.Label(self.canvas1, text='CLIENT NAME', font=('Bahnschrift',10))
        self.canvas1.create_window(600, 100, window=self.client_label)

        self.client_name = tkinter.Entry (self.canvas1) 
        self.canvas1.create_window(600, 120, window=self.client_name)  

        self.button1 = tkinter.Button(text='Find Client',font=('Bahnschrift',10), command=self.file_opener)
        self.canvas1.create_window(600, 150, window=self.button1)

    def file_opener(self):
        self.name = self.user_name.get()
        self.xl= pd.read_excel(f"C:/Users/leven\Desktop/{self.name}'s Portfolio.xlsm", sheet_name='CM DATA')
        self.client = self.client_name.get()
        self.result = self.xl[self.xl.Client.str.contains(self.client, regex=False, case=False)][['Client','Column2','Column3','Column4','Column5']]
        self.client_name.delete(0, 'end')
        self.active_state=[]
        for widget in self.canvas2.winfo_children():
            widget.destroy()
        for x in range(len(self.result)):
            for y in range(len(self.result.columns)):
                textbox = Text(self.canvas2, width=20, height=2,font=('Bahnschrift',10))
                textbox.grid(row=x,column=y, padx=2, pady=2)
                textbox.insert(END, self.result.iloc[x,y])
            var = tkinter.IntVar()
            self.checkbox = Checkbutton(self.canvas2,variable=var, onvalue=1, offvalue=0, relief=SUNKEN)
            self.checkbox.grid(row=x, column=6, padx=2, pady=2, ipadx=2, ipady=2)
            self.active_state.append(var)

if __name__ == "__main__":
    root = Tk()
    my_gui = App(root)
    root.mainloop()

使用 tkinter 使滚动条工作可能很尴尬,尤其是在考虑网格权重 () 等问题时​​。

我在下面创建了您的代码的最小版本,但为简单起见,将条目和按钮 canvas 的布局替换为框架,同时也最小化了文件打开器功能,现在只需要要在下面的代码中添加到 运行.

的文件路径

我在 canvas/scrollbar gui 部分添加了一些评论,以便更容易直观地显示元素的位置以及它们之间的关系。

import tkinter
from tkinter import *
import pandas as pd


class App:
    def __init__(self, window):
        self.window = window

        window.columnconfigure(0, weight=1)
        window.rowconfigure(1, weight=1)

        window.title("Excel Magician")
        window.geometry("800x800")

        # === GUI for entries & button ===

        # Main frame
        self.frame = tkinter.Frame(window)
        self.frame.grid(row=0, column=0, columnspan=2, sticky='w')
        # label User
        self.user_label = tkinter.Label(self.frame, text='USERNAME')
        self.user_label.grid(row=0, column=0)
        # entry User
        self.user_name = tkinter.Entry(self.frame)
        self.user_name.grid(row=0, column=1)
        # label Client
        self.client_label = tkinter.Label(self.frame, text='CLIENT NAME')
        self.client_label.grid(row=1, column=0)
        # Entry Client
        self.client_name = tkinter.Entry(self.frame)
        self.client_name.grid(row=1, column=1)
        # Button
        self.button1 = tkinter.Button(self.frame, text='Find Client', command=self.file_opener)
        self.button1.grid(row=2, column=1)

        # === GUI for canvas and scrollbar ===

        # 1) Create a frame (may not be necessary depending on grid method used)
        self.frame1 = tkinter.Frame(window)
        self.frame1.grid(row=1, column=0, sticky='nsew')
        self.frame1.columnconfigure(0, weight=1)
        self.frame1.rowconfigure(1, weight=1)

        # 2) Canvas goes inside frame
        self.canvas = tkinter.Canvas(self.frame1)
        self.canvas.grid(row=1, column=0, sticky='nsew')

        # 3) Scroll Sidebar also goes inside this frame in next column
        self.scroll_y = tkinter.Scrollbar(self.frame1, orient='vertical', command=self.canvas.yview)
        self.scroll_y.grid(row=1, column=1, sticky='ns')

        # 4) Sub-frame goes inside of the canvas
        self.canvas_sub_frame = tkinter.Frame(self.canvas)

        # Column Header population
        self.column_list = ['CLIENT', 'Column2', 'Column3', 'Column4', 'Column5']
        for i in range(len(self.column_list)):
            tkinter.Label(self.canvas_sub_frame, text=self.column_list[i]).grid(row=0, column=i, sticky='ew')
            self.canvas_sub_frame.columnconfigure(i, weight=1)

        # 5) Create Scroll Y & table expansion events
        self.canvas.create_window(0, 0, anchor='nw', window=self.canvas_sub_frame, tag='window')
        self.canvas_sub_frame.bind('<Configure>', self.config_frame)  # configure to allow scrolling
        self.canvas.bind('<Configure>', self.canvas_config)  # configures to allow expansion of window

    # Scroll bar function
    def config_frame(self, event):
        self.canvas.configure(scrollregion=self.canvas.bbox('all'), yscrollcommand=self.scroll_y.set)

    # Resizing function
    def canvas_config(self, event):
        canvas_width = event.width
        event.widget.itemconfig('window', width=canvas_width)

    def file_opener(self):
        xl = pd.read_excel(r'Filepath\Test_File.xlsx', sheet_name='Sheet1')

        for x in range(len(xl)):
            for y in range(len(xl.columns)):
                textbox = Text(self.canvas_sub_frame, width=15, height=2)
                textbox.grid(row=x+1, column=y, sticky='ew')
                textbox.insert(END, xl.iloc[x, y])
            checkbox = Checkbutton(self.canvas_sub_frame)
            checkbox.grid(row=x+1, column=6)


if __name__ == "__main__":
    root = Tk()
    my_gui = App(root)
    root.mainloop()

由于使用 grid()pack() 将小部件放入 canvas 不会更改 scrollregion,因此链接到 canvas 的滚动条不会被激活。

您需要创建一个框架并使用 .create_window(...) 将其放入 canvas,然后将那些 TextCheckbutton 小部件放入该框架。此外,您还需要在调整框架大小时更新 canvas 的 scrollregion,以便可以激活附加的滚动条。

以下是根据您的修改后的代码:

import tkinter
import pandas as pd

class App:
    def __init__(self, window):
        self.window = window

        window.title("Excel Magician")
        window.geometry("800x800")

        self.canvas1 = tkinter.Canvas(window, width = 720, height = 200)
        self.canvas1.grid(row=0, column=0)

        self.frame1 = tkinter.Frame(window, width = 720,height = 20, bg = '#0ca274')
        self.frame1.grid(row=1, column=0, pady=4)

        self.canvas2 = tkinter.Canvas(window, width = 720,height = 300, bg = '#0ca274')
        self.canvas2.grid(row=2, column=0, pady=1, sticky='ew') # added sticky='ew'

        self.column_list = ['CLIENT','Column2','Column3','Column4','Column5']
        for i in range(len(self.column_list)):
            tkinter.Label(self.frame1, text= self.column_list[i], font=('Bahnschrift',10)).grid(row= 0, column= i, sticky='e', ipadx=50)

        self.user_label = tkinter.Label(self.canvas1, text='USERNAME', font=('Bahnschrift',10))
        self.canvas1.create_window(600, 60, window=self.user_label)

        self.user_name = tkinter.Entry (self.canvas1)
        self.canvas1.create_window(600, 80, window=self.user_name)

        self.client_label = tkinter.Label(self.canvas1, text='CLIENT NAME', font=('Bahnschrift',10))
        self.canvas1.create_window(600, 100, window=self.client_label)

        self.client_name = tkinter.Entry (self.canvas1)
        self.canvas1.create_window(600, 120, window=self.client_name)

        self.button1 = tkinter.Button(text='Find Client',font=('Bahnschrift',10), command=self.file_opener)
        self.canvas1.create_window(600, 150, window=self.button1)

        # create the scrollable frame and the scrollbar
        self.internal = tkinter.Frame(self.canvas2)
        self.internal.bind('<Configure>', lambda e: self.canvas2.config(scrollregion=self.canvas2.bbox('all')))
        self.canvas2.create_window(0, 0, window=self.internal, anchor='nw')

        self.scrollbar = tkinter.Scrollbar(window, command=self.canvas2.yview)
        self.scrollbar.grid(row=2, column=1, sticky='ns')
        self.canvas2.config(yscrollcommand=self.scrollbar.set)

    def file_opener(self):
        self.name = self.user_name.get()
        self.xl= pd.read_excel(f"C:/Users/leven/Desktop/{self.name}'s Portfolio.xlsm", sheet_name='CM DATA')
        self.client = self.client_name.get()
        self.result = self.xl[self.xl.Client.str.contains(self.client, regex=False, case=False)][['Client','Column2','Column3','Column4','Column5']]
        self.client_name.delete(0, 'end')
        self.active_state=[]
        # clear existing widgets in self.internal
        for widget in self.internal.winfo_children():
            widget.destroy()
        for x in range(len(self.result)):
            for y in range(len(self.result.columns)):
                # created inside self.internal
                textbox = tkinter.Text(self.internal, width=20, height=2,font=('Bahnschrift',10))
                textbox.grid(row=x,column=y, padx=2, pady=2)
                textbox.insert(tkinter.END, self.result.iloc[x,y])
            var = tkinter.IntVar()
            # created inside self.internal
            self.checkbox = tkinter.Checkbutton(self.internal,variable=var, onvalue=1, offvalue=0, relief=tkinter.SUNKEN)
            self.checkbox.grid(row=x, column=6, padx=2, pady=2, ipadx=2, ipady=2)
            self.active_state.append(var)

if __name__ == "__main__":
    root = tkinter.Tk()
    my_gui = App(root)
    root.mainloop()