无法创建非常宽的 canvas

Can't create a very wide canvas

我正在尝试生成任意数量的行,其中每行在左侧包含一个标签,在右侧包含一个滚动的 canvas。我的代码如下:

import tkinter as tk

class App(tk.Frame):
    def __init__(self, root):
        super(App, self).__init__(root)
        self.root = root

        # CREATE OUTER FRAMES #

        self.main_frame = MainFrame(root)
        self.main_frame.pack(padx=0, pady=0)

        self.RowCollection = RowCollection(root, self.main_frame)
        self.row_1 = self.RowCollection.row()
        self.row_2 = self.RowCollection.row()
        self.row_3 = self.RowCollection.row()

        def scroll_x(*args):
            # link scrollbar to canvas' xviex
            self.row_1.canvas.xview(*args)
            self.row_2.canvas.xview(*args)
            self.row_3.canvas.xview(*args)

        self.scrollbar = tk.Scrollbar(self.RowCollection.right_frame, orient=tk.HORIZONTAL)
        self.scrollbar.config(command=scroll_x)
        self.scrollbar.grid(row=Row.row_count, column=0, sticky='ew')

        canvas_1 = self.row_1.canvas
        canvas_1.config(xscrollcommand=self.scrollbar.set)
        canvas_1.bind('<Configure>', lambda event: canvas_1.configure(scrollregion=canvas_1.bbox('all')))

        canvas_2 = self.row_2.canvas
        canvas_2.config(xscrollcommand=self.scrollbar.set)
        canvas_2.bind('<Configure>', lambda event: canvas_2.configure(scrollregion=canvas_2.bbox('all')))

        canvas_3 = self.row_3.canvas
        canvas_3.config(xscrollcommand=self.scrollbar.set)
        canvas_3.bind('<Configure>', lambda event: canvas_3.configure(scrollregion=canvas_3.bbox('all')))


        # WINDOW_WIDTH = 1000
        WINDOW_HEIGHT = 700
        WINDOW_WIDTH = root.winfo_screenwidth() - 30
        root.geometry(f'{WINDOW_WIDTH}x{WINDOW_HEIGHT}+0+0')
        self.pack(side="top")


class MainFrame(tk.Frame):
    def __init__(self, master):
        tk.Frame.__init__(self, master, bg='blue')


class RowCollection:
    """Collection of rows"""

    def __init__(self, root, frame):
        self.row_list = []
        self.root = root
        self.frame = frame
        self.right_frame = tk.Frame(self.frame, bg='red', width=10000)
        self.right_frame.pack(side=tk.RIGHT)
        self.left_frame = tk.Frame(self.frame)
        self.left_frame.pack(side=tk.LEFT, fill=tk.Y)

    def row(self):
        row = Row(self)
        self.row_list.append(row)

        return row

class Row:
    """Every row consists of a label on the left side and a canvas on the right side"""

    row_count = 0
    label_width = 15
    line_weight = 3
    line_yoffset = 3
    padx = 20

    def __init__(self, collection):
        self.frame = collection.frame
        self.root = collection.root
        self.collection = collection
        self.canvas = None
        self.label = None
        self.text = f'Canvas {Row.row_count}'
        self.height = 100

        self.root.update()

        self.label = tk.Label(self.collection.left_frame,
                              text=self.text,
                              height=1,
                              width=Row.label_width,
                              relief='raised')
        self.label.grid(row=Row.row_count, column=0, sticky='ns')

        # configure row size to match future canvas height
        self.collection.left_frame.grid_rowconfigure(Row.row_count, minsize=self.height)

        self.root.update()

        self.canvas = tk.Canvas(self.collection.right_frame,
                             width=self.root.winfo_width() - self.label.winfo_width(),
                             height=self.height,
                             bg='white',
                             highlightthickness=0)

        self.canvas.grid(row=Row.row_count, column=0, sticky='ew')

        self.root.update()

        # draw line
        self.line = self.canvas.create_rectangle(self.padx,
                                                 self.canvas.winfo_height() - Row.line_yoffset,
                                                 self.canvas.winfo_width() - self.padx,
                                                 self.canvas.winfo_height() - Row.line_yoffset + Row.line_weight,
                                                 fill='#000000', width=0, tags='line')


        # Create point at canvas edge to prevent scrolling from removing padding
        self.bounding_point = self.canvas.create_rectangle(0, 0, 0, 0, width=0)
        self.bounding_point = self.canvas.create_rectangle(self.canvas.winfo_width(), self.canvas.winfo_width(),
                                                           self.canvas.winfo_width(), self.canvas.winfo_width(),
                                                           width=0)

        # set column weight to 1 so it expands
        self.collection.right_frame.grid_columnconfigure(0, weight=1)

        # config canvas
        self.canvas.config(scrollregion=self.canvas.bbox('all'))

        Row.row_count += 1


if __name__ == '__main__':

    root = tk.Tk()
    
    app = App(root)
    
    root.mainloop()

我创建了一个主框架,有一个左右框架作为子框架。左框架包含标签,右框架包含 canvases.

我需要有创建非常宽的 canvases 的选项,但是增加右框架的宽度选项似乎对 canvas 大小在 3000 左右的某个值后没有影响。怎么回事?

此外,我希望标签始终可见,但目前,增加 canvases 大小最终会使标签超出可见范围。

I need to have the option to create very wide canvases, but increasing the width option of the right frame seems to have no effect on the canvas size after some value around 3000. What is going on?

您正在将 canvas 添加到具有 grid 的框架中。默认情况下,框架会扩大或缩小以适应 canvas。因此,无论您将框架制作多大,它都会缩小到大致 canvas 的大小(and/or 框架中 space 的大小 parent ,具体取决于您将其添加到 parent) 的方式。

如果您希望 canvas 具有特定大小,最好的办法是为 canvas 指定该大小,而不是依赖于其包含的小部件的宽度。

如果您真的想通过控制框架的大小来控制 canvas 的大小,您可以告诉框架不要 扩大或缩小以适合它 children。您可以通过关闭 geometry propagation 来实现。这告诉框架忽略其 children 的大小。这很少是正确的解决方案,因为它通常需要您做更多的工作才能制作响应式 GUI。尽管如此,要关闭几何传播,请执行以下操作:

self.collection.right_frame.grid_propagate(False)