简体   繁体   English

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

[英]Scrollbar in Canvas doesn't work - Tkinter Python

I have been struggling to add a working scrollbar to my canvas widget for days.几天来,我一直在努力为我的 canvas 小部件添加一个工作滚动条。 I'm working on a small app to show excel data.我正在开发一个小应用程序来显示 excel 数据。 I have tried so many things but couldn't achieve a working result:/我已经尝试了很多东西,但无法达到工作结果:/

I populate canvas2 with "file_opener" function, and I would like to add the scrollbar to the 7th column of canvas2.我用“file_opener”function 填充 canvas2,我想将滚动条添加到 canvas2 的第 7 列。 However, in my previous attempts the scrollbar was appearing only in row 0 and without the functionality.但是,在我之前的尝试中,滚动条只出现在第 0 行,并且没有该功能。

I would greatly appreciate your help as I'm a self-learning beginner.我非常感谢您的帮助,因为我是一个自学初学者。

This is how it looks: "https://ibb.co/W2d674g"这就是它的外观:“https://ibb.co/W2d674g”

Here is my code:这是我的代码:

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()

As putting widgets into a canvas using grid() or pack() does not change the scrollregion , so the scrollbar linked to the canvas will not be activated.由于使用grid()pack()将小部件放入 canvas 不会更改滚动scrollregion ,因此不会激活链接到 canvas 的滚动条。

You need to create a frame and put it into the canvas using .create_window(...) and then put those Text and Checkbutton widgets into this frame.您需要创建一个框架并使用.create_window(...)将其放入 canvas 中,然后将这些TextCheckbutton小部件放入此框架中。 Also you need to update the scrollregion of the canvas when the frame is resized, so that the attached scrollbar can be activated.此外,您还需要在调整框架大小时更新scrollregion的滚动区域,以便激活附加的滚动条。

Below is a modified code based on yours:以下是根据您的修改后的代码:

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()

Getting scrollbars to work can be awkward with tkinter, especially when also accounting for things like grid weight ( I asked a question about that myself recently ).使用 tkinter 让滚动条工作可能会很尴尬,尤其是在考虑网格重量等因素时( 我最近问了一个关于这个问题的问题)。

I've created a minimal version of your code below but replaced the layout of the entries & button canvas with a frame for simplicity, as well as also minimizing the file opener function, which now just needs the filepath to be added in the code below to run.我在下面创建了您的代码的最小版本,但为简单起见,用框架替换了条目和按钮 canvas 的布局,同时还最小化了文件打开器 function,现在只需要在下面的代码中添加文件路径跑步。

I've added some comments to the canvas/scrollbar gui section just so its easier to visualise placement of the elements and how they relate to one another.我在 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')
        self.canvas.columnconfigure(0, weight=1)

        # 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')
        self.scroll_y.columnconfigure(0, weight=1)

        # 4) Sub-frame goes inside of the canvas
        self.canvas_sub_frame = tkinter.Frame(self.canvas)
        self.canvas_sub_frame.grid(row=1, column=0)
        self.canvas_sub_frame.rowconfigure(0, weight=1)
        # 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

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

    # 5) 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\file.xlsx')

        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()

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM