繁体   English   中英

Tkinter-调整包含其他小部件的小部件的大小

[英]Tkinter - resizing widget containing other widgets

我正在尝试构建看起来像的UI

------------------------------| s |
Label-1 | Canvas-1            | c |
------------------------------| r |
Label-2 | Canvas-2            | o |
------------------------------| l |
Label-3 | Canvas-3            | l |
------------------------------|   |
 . . .                        | Y |
Label-n | Canvas-n            |   |
------------------------------|   |
        | scrollbar-horizontal|   |
------------------------------|   |

使用下面发布的代码,我能够做到。 我唯一缺少的是能够使用Canvas对象调整零件尺寸同时保持y-scroll-bar功能的功能。 我找到了一种通过取消注释self.frame.pack(expand=True, fill='x')来启用调整大小的方法,但是y-scroll-bar不起作用。 另外,我可以使用y-scroll-bar,但是重新调整Canvas对象的大小并不能正常工作,当我放大窗口时,可以看到白色的空白,如下所示。

也许值得一提的是,我的代码中的内部Frame使用了网格管理器,并且我尝试同时使用grid_rowconfiguregrid_columnconfigure来获得所需的内容。 但是我无法使其工作。

我想知道如何同时工作。

import tkinter
import tkinter.font


class LineData(object):

    def __init__(self, desc, box):
        self.desc = desc
        self.box = box


class LinePlotter(object):

    def __init__(self, label, canvas, desc, box):
        self.label = label
        self.canvas = canvas
        self.desc = desc
        self.box = box

    def plot(self):
        self.label.set(self.desc)
        x1, y1 = self.box[0], 1
        x2, y2 = self.box[1], 20
        self.canvas.create_rectangle(x1, y1, x2, y2, outline="#D1D0CE",
                                     width=4, fill="#D1D0CE")

        self.canvas.create_rectangle(x2+100, y1, x2+(x2-x1)+100, y2, outline="#D1D0CE",
                                     width=4, fill="#D1D0CE")


class Plotter(tkinter.Frame):

    def __init__(self, parent, lines_data):
        super().__init__(root)
        self.root = root

        self.outer_canvas = tkinter.Canvas(root, borderwidth=0, background="#ffffff")
        self.frame = tkinter.Frame(self.outer_canvas, background="#ffffff")

        self.scrollY = tkinter.Scrollbar(root, orient=tkinter.VERTICAL, command=self.outer_canvas.yview)
        self.outer_canvas.configure(yscrollcommand=self.scrollY.set)
        self.scrollY.pack(side="right", fill="y", expand=False)

        self.outer_canvas.pack(side="left", fill="both", expand=True)

        self.outer_canvas.create_window((4, 4), window=self.frame, anchor="nw",
                                  tags="self.frame")

        # uncommenting this parts allows to resize Canvas part but then y-scroll-bar does not work
        # self.frame.pack(expand=True, fill='x')
        self.frame.bind("<Configure>", self.onFrameConfigure)

        self.lines_data = lines_data

    def onFrameConfigure(self, event):
        self.outer_canvas.configure(scrollregion=self.outer_canvas.bbox("all"))

    def configure_xscroll_bar(self):
        x1, y1, x2, y2 = self.canvases[0].bbox(tkinter.ALL)
        x1 = y1 = 0
        for c in self.canvases:
            t1, u1, t2, u2 = c.bbox(tkinter.ALL)
            x1 = min(x1, t1)
            x2 = max(x2, t2)
            y1 = min(y1, u1)
            y2 = max(y2, u2)

        for c in self.canvases:
            c.config(scrollregion=(x1-5, y1-5, x2+5, y2+5))

    def xview(self, *args):
        for c in self.canvases:
            c.xview(*args)

    def create_x_scroll_bars(self):
        self.scrollX = tkinter.Scrollbar(self.frame, orient=tkinter.HORIZONTAL)

        self.scrollX['command'] = self.xview
        l = len(self.lines_data)
        self.scrollX.grid(row=l, column=1, sticky='NSEW')

    def initUI(self):

        self.create_x_scroll_bars()

        self.canvases = []

        h = 40
        for row_idx, line_data in enumerate(self.lines_data):
            desc = tkinter.StringVar()
            font = tkinter.font.Font(size=11)
            label = tkinter.Label(self.frame, textvariable=desc, font=font)
            label.grid(row=row_idx, column=0, sticky=tkinter.W)

            canvas = tkinter.Canvas(self.frame, bg='white',
                                    xscrollcommand=self.scrollX.set,
                                    height=h)

            line = LinePlotter(desc, canvas, line_data.desc, line_data.box)
            line.plot()

            canvas.grid(row=row_idx, column=1, sticky='NSEW')
            self.canvases.append(canvas)

        self.configure_xscroll_bar()

        # for row_idx in range(len(self.lines_data)+1):
        #     self.frame.grid_rowconfigure(row_idx, weight=1)

        # self.frame.grid_columnconfigure(1, weight=1)

        self.pack(expand=True, fill=tkinter.BOTH)


def prepare_data():
    lst = []
    for i in range(1, 20):
        d = "{}".format(i)
        x1 = (i+1)*20
        x2 = x1+30
        l = LineData(d, (x1, x2))
        lst.append(l)
    return lst


root = tkinter.Tk()
Plotter(root, prepare_data()).initUI()
root.mainloop()

在此处输入图片说明

我本人能够解决问题,所以让我分享使它起作用的工具:

  • 不使用包管理器,而是用于所有小部件和框架网格管理器;
  • 正确使用“粘性”选项,以便正确放置和扩展小部件;
  • 使用`itemconfig`来改变`onFrameConfigure`函数中的框架属性;
  • 为不同的窗口小部件设置不同的背景颜色对于了解哪些窗口小部件的行为不符合预期非常有帮助。

下面是修改后的工作代码。

import tkinter
import tkinter.font


class LineData(object):

    def __init__(self, desc, box):
        self.desc = desc
        self.box = box


class LinePlotter(object):

    def __init__(self, label, canvas, desc, box):
        self.label = label
        self.canvas = canvas
        self.desc = desc
        self.box = box

    def plot(self):
        self.label.set(self.desc)
        x1, y1 = self.box[0], 1
        x2, y2 = self.box[1], 20
        self.canvas.create_rectangle(x1, y1, x2, y2, outline="#D1D0CE",
                                     width=4, fill="#D1D0CE")

        self.canvas.create_rectangle(x2+100, y1, x2+(x2-x1)+100, y2, outline="#D1D0CE",
                                     width=4, fill="#D1D0CE")


class Plotter(tkinter.Frame):

    def __init__(self, root, lines_data):
        super().__init__(root, background="green")
        self.root = root
        self.root.grid_columnconfigure(index=0, weight=1)
        self.root.grid_rowconfigure(index=0, weight=1)

        self.outer_canvas = tkinter.Canvas(root, borderwidth=0, background="blue")
        self.outer_canvas.grid(row=0, column=0, sticky="WENS")

        self.scrollY = tkinter.Scrollbar(root, orient=tkinter.VERTICAL, command=self.outer_canvas.yview, background="red")
        self.outer_canvas.configure(yscrollcommand=self.scrollY.set)
        self.scrollY.grid(row=0, column=1, sticky="NSE")

        self.frame = tkinter.Frame(self.outer_canvas, background="black")
        self.frame_id = self.outer_canvas.create_window((4, 4), window=self.frame, anchor="nw",
                                  tags="self.frame")

        self.frame.bind("<Configure>", self.onFrameConfigure)
        self.outer_canvas.bind('<Configure>', self.onFrameConfigure)
        self.lines_data = lines_data

    def onFrameConfigure(self, event):

        f_reqsize = (self.frame.winfo_reqwidth(), self.frame.winfo_reqheight())
        c_size = (self.outer_canvas.winfo_width(), self.outer_canvas.winfo_height())
        f_width = max(f_reqsize[0], c_size[0])
        f_height = max(f_reqsize[1], c_size[1])

        self.outer_canvas.config(scrollregion=self.outer_canvas.bbox("all"))
        self.outer_canvas.itemconfig(self.frame_id, width=f_width, height=f_height)

    def configure_xscroll_bar(self):
        x1, y1, x2, y2 = self.canvases[0].bbox(tkinter.ALL)
        x1 = y1 = 0
        for c in self.canvases:
            t1, u1, t2, u2 = c.bbox(tkinter.ALL)
            x1 = min(x1, t1)
            x2 = max(x2, t2)
            y1 = min(y1, u1)
            y2 = max(y2, u2)

        for c in self.canvases:
            c.config(scrollregion=(x1-5, y1-5, x2+5, y2+5))

    def xview(self, *args):
        for c in self.canvases:
            c.xview(*args)

    def create_x_scroll_bars(self):
        self.scrollX = tkinter.Scrollbar(self.frame, orient=tkinter.HORIZONTAL)

        self.scrollX['command'] = self.xview
        l = len(self.lines_data)
        self.scrollX.grid(row=l, column=1, sticky='NSEW')

    def initUI(self):

        self.create_x_scroll_bars()

        self.canvases = []

        self.frame.grid_columnconfigure(1, weight=1)

        h = 40
        for row_idx, line_data in enumerate(self.lines_data):
            desc = tkinter.StringVar()
            font = tkinter.font.Font(size=11)
            label = tkinter.Label(self.frame, textvariable=desc, font=font)
            label.grid(row=row_idx, column=0, sticky=tkinter.W)

            canvas = tkinter.Canvas(self.frame, bg='white',
                                    xscrollcommand=self.scrollX.set,
                                    height=h)

            line = LinePlotter(desc, canvas, line_data.desc, line_data.box)
            line.plot()

            canvas.grid(row=row_idx, column=1, sticky='NSEW')
            self.canvases.append(canvas)

        self.configure_xscroll_bar()


def prepare_data():
    lst = []
    for i in range(1, 20):
        d = "{}".format(i)
        x1 = (i+1)*20
        x2 = x1+30
        l = LineData(d, (x1, x2))
        lst.append(l)
    return lst


root = tkinter.Tk()
Plotter(root, prepare_data()).initUI()
root.mainloop()

暂无
暂无

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

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