【问题标题】:Can't create a very wide canvas无法创建非常宽的画布
【发布时间】:2021-07-15 23:09:27
【问题描述】:

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

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

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

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

此外,我希望标签始终可见,但目前,增加画布大小最终会将标签推到可见性之外。

【问题讨论】:

    标签: python tkinter tkinter-canvas tkinter-layout


    【解决方案1】:

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

    您正在将画布添加到带有grid 的框架中。默认情况下,框架会扩大或缩小以适合画布。因此,无论您制作多大的框架,它都会缩小到大约画布的大小(和/或框架父级中的空间大小,具体取决于您将其添加到父级的方式)。

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

    如果您真的想通过控制框架的大小来控制画布的大小,您可以告诉框架不要放大或缩小以适应其子级。您可以通过关闭几何传播来做到这一点。这告诉框架忽略其子级的大小。这很少是正确的解决方案,因为它通常需要您做更多的工作来制作响应式 GUI。不过,要关闭几何传播,请执行以下操作:

    self.collection.right_frame.grid_propagate(False)
    

    【讨论】:

    • 非常感谢!出于某种原因,我还不得不将grid_columnconfigure 语句移到脚本的末尾,否则滚动条槽的大小会在我单击它时立即改变。这给我留下了标签被推开的问题。对此的任何帮助也将不胜感激。
    • 顺便说一句,我不想​​通过控制框架的大小来控制画布的大小。那是我的糟糕设计。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-10-10
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多