【问题标题】:TKInter - Confused about frames and scrollingTKInter - 对帧和滚动感到困惑
【发布时间】:2020-06-17 19:39:21
【问题描述】:

我正在尝试编写一个具有三个框架的 tkinter 应用程序 - 一个顶部框架,用户在其中输入一些文本,一个动态构造的中间部分,其中对文本进行一些预分析,以及一个底部框架,其中一次用户在中间部分选择了他们想要的选项,将产生输出。

问题在于,根据输入,可能会显示大约 10-20 行(最坏的情况下是 30 行),而在小型监视器上,输出会从屏幕上消失。

我希望无论屏幕如何调整大小,顶部(输入)和底部(输出)框架都可见,并且中间部分滚动(如果需要)并且仍然允许用户选择他们的选择。

我很困惑如何在调整屏幕大小时调整中间部分的大小,如果需要显示滚动条,并且仍然允许访问所有内容。

我在这里创建了一个缩减版(为简单起见,我删除了处理方法,而是在一个类似于实际中间部分的循环中创建了一些假输出)。

请忽略可怕的配色方案 - 我只是想了解哪个框架去了哪里(我会尽快删除颜色!)

感谢您的任何建议...

import tkinter as tk
from tkinter import scrolledtext

class MyApp(tk.Tk):
    def __init__(self, title="Sample App", *args, **kwargs):
        tk.Tk.__init__(self, *args, **kwargs)

        self.title(title)
        self.configure(background="Gray")
        self.columnconfigure(0, weight=1)
        self.rowconfigure(0, weight=1)

        # Create the overall frame:
        master_frame = tk.Frame(self, bg="Light Blue", bd=3, relief=tk.RIDGE)
        master_frame.grid(sticky=tk.NSEW)
        master_frame.rowconfigure([0, 2], minsize=90)  # Set min size for top and bottom
        master_frame.rowconfigure(1, weight=1)  # Row 1 should adjust to window size
        master_frame.columnconfigure(0, weight=1)  # Column 0 should adjust to window size

        # Create the frame to hold the input field and action button:
        input_frame = tk.LabelFrame(master_frame, text="Input Section", bg="Green", bd=2, relief=tk.GROOVE)
        input_frame.grid(row=0, column=0, padx = 5, pady = 5, sticky=tk.NSEW)
        input_frame.columnconfigure(0, weight=1)
        input_frame.rowconfigure(0, weight=1)

        # Create a frame for the middle (processing) section.
        middle_frame = tk.LabelFrame(master_frame, text = "Processing Section")
        middle_frame.grid(row=1, column=0, padx=5, pady=5, sticky=tk.NSEW)

        # Create the frame to hold the output:
        output_frame = tk.LabelFrame(master_frame, text="Output Section", bg="Blue", bd=2, relief=tk.GROOVE)
        output_frame.grid(row=2, column=0, columnspan=3, padx=5, pady=5, sticky=tk.NSEW)
        output_frame.columnconfigure(0, weight=1)
        output_frame.rowconfigure(0, weight=1)

        # Add a canvas in the middle frame.
        self.canvas = tk.Canvas(middle_frame, bg="Yellow")
        self.canvas.grid(row=0, column=0)

        # Create a vertical scrollbar linked to the canvas.
        vsbar = tk.Scrollbar(middle_frame, orient=tk.VERTICAL, command=self.canvas.yview)
        vsbar.grid(row=0, column=1, sticky=tk.NS)
        self.canvas.configure(yscrollcommand=vsbar.set)

        # Content for the input frame, (one label, one input box and one button).
        tk.Label(input_frame,
              text="Please type, or paste, the text to be analysed into this box:").grid(row=0, columnspan = 3, sticky=tk.NSEW)
        self.input_box = scrolledtext.ScrolledText(input_frame, height=5, wrap=tk.WORD)
        self.input_box.columnconfigure(0, weight=1)
        self.input_box.grid(row=1, column=0, columnspan = 3, sticky=tk.NSEW)
        tk.Button(input_frame,
               text="Do it!",
               command=self.draw_choices).grid(row=2, column=2, sticky=tk.E)

        # Content for the output frame, (one text box only).
        self.output_box = scrolledtext.ScrolledText(output_frame, width=40, height=5, wrap=tk.WORD)
        self.output_box.grid(row=0, column=0, columnspan=3, sticky=tk.NSEW)


    def draw_choices(self):
         """ This method will dynamically create the content for the middle frame"""
         self.option = tk.IntVar()  # Variable used to hold user's choice
         self.get_input_text()
         for i in range(30):
             tk.Radiobutton(self.canvas,
                        text=f"Option {i + 1}: ", variable=self.option,
                        value=i,
                        command=self.do_analysis
                        ).grid(row=i, column=0, sticky=tk.W)
             tk.Label(self.canvas,
                  text=f"If you pick Option {i + 1}, the output will look like this: {self.shortText}.",
                   anchor=tk.W
                   ).grid(row=i, column=1, sticky=tk.W)
         self.canvas.configure(scrollregion=self.canvas.bbox("all"))


    def get_input_text(self):
         """ Will get the text from the input box and also create a shortened version to display on one line"""
         screenWidth = 78
         self.input_text = self.input_box.get(0.0, tk.END)

         if len(self.input_text) > screenWidth:
             self.shortText = self.input_text[:screenWidth]
         else:
             self.shortText = self.input_text[:]
         self.shortText = self.shortText.replace('\n', ' ')  # strip out carriage returns just in case

    def do_analysis(self):
        """This will ultimately process and display the results"""
        option = self.option.get()  # Get option from radio button press
        output_txt = f"You picked option {option + 1} and here is the output: \n{self.input_text}"
        self.output_box.delete(0.0, tk.END)
        self.output_box.insert(0.0, output_txt)


if __name__ == "__main__":
    app = MyApp("My Simple Text Analysis Program")
    app.mainloop()

【问题讨论】:

  • 是的 - 谢谢!我认为这两个链接中的第二个正是我正在寻找的(我一定在我所有的谷歌搜索中都错过了它)。但是,Bryan Oakley 出色的解决方案混合了包和网格几何,并且不会(按原样)在我的代码上工作,因为我的根窗口顶部有一个主框架。然而,无论如何我都会重构我的代码并摆脱主框架(因为我意识到我不再需要那个设置)并遵循 Bryan 的结构。我需要一段时间才能完成,但我会回来报告...谢谢!
  • 仍在苦苦挣扎...关注 @ bryan Oakley 的优秀帖子:使用 this answer 重试,滚动部分 独立于要滚动的小部件。也去掉你的例子并展示你的尝试。
  • @stovfl 这真是太棒了!非常感谢!这正是我一直在尝试(并且失败)实现的目标。我还处于与 tkinter 的学习之旅的早期阶段,这对我帮助很大。再次感谢您。

标签: python-3.x tkinter canvas tkinter-canvas frames


【解决方案1】:

我知道您不能在同一个容器中混合使用网格和打包几何图形,并且滚动条必须附加到画布上,因此要放置在该画布上的对象必须在另一个容器中,因此,尝试按照 Bryan 的示例,我创建了我想要的最小版本 - 具有三个部分的窗口 - 顶部、中间和底部。顶部和底部部分将包含一个简单的文本字段,中间部分将包含动态内容并且必须能够根据需要滚动。


进口

import tkinter as tk

class App(tk.Tk):
    def __init__(self):
        super().__init__()
        self.title("A simple GUI")

        # Top frame
        self.top_frame = tk.Frame(self, bg="LIGHT GREEN")
        self.top_frame.pack(fill=tk.X)
        tk.Label(self.top_frame, bg=self.top_frame.cget('bg'),
                 text="This is a label on the top frame")\
            .grid(row=0, columnspan=3, sticky=tk.NSEW)

        # Middle Frame
        # Import from https://stackoverflow.com/a/62446457/7414759
        # and don't change anything
        sbf = ScrollbarFrame(self, bg="LIGHT BLUE")
        sbf.pack(fill=tk.X, expand=True)

        # self.middle_frame = tk.Frame(self, bg="LIGHT BLUE")
        self.middle_frame = sbf.scrolled_frame

        # Force scrolling by adding multiple Label
        for _ in range(25):
            tk.Label(self.middle_frame, bg=self.middle_frame.cget('bg'),
                     text="This is a label on the dynamic (middle) section")\
                .grid()

        # Bottom Frame
        self.bottom_frame = tk.Frame(self, bg="WHITE")
        self.bottom_frame.pack(fill=tk.X)
        tk.Label(self.bottom_frame, bg=self.bottom_frame.cget('bg'),
                 text="This is a label on the bottom section")\
            .grid(row=0, columnspan=3, sticky=tk.NSEW)


if __name__ == '__main__':
    App().mainloop()

【讨论】:

  • 您的主要问题是,您想在一个函数中完成all,但您没有意识到必须按原样使用链接的滚动条示例。如果您只将部分移动到您的部分,您将破坏滚动条部分。
  • 谢谢@stovfl!我已经设法使用了您出色的 ScrollbarFrame 类(实际上我稍微更改了它以从 LabelFrame 继承而不是 frame,因为我想在我的应用程序中使用 LabelFrames)但是在您友好地编辑我的帖子之前我已经让它工作了。请编辑我的帖子。再次感谢!
猜你喜欢
  • 1970-01-01
  • 2012-12-17
  • 2021-11-28
  • 1970-01-01
  • 2011-10-17
  • 1970-01-01
  • 2013-03-29
  • 2014-11-02
  • 1970-01-01
相关资源
最近更新 更多