python-3.x Tkinter中的滚动行为问题

zazmityj  于 2023-08-08  发布在  Python
关注(0)|答案(1)|浏览(124)

我在基于Tkinter的终端界面中遇到了滚动行为问题。我正在使用scrolledtext.ScrolledText小部件来显示输出,我希望实现以下行为:
1.当用户手动向上滚动时,文本区域应该保持在该位置,即使窗口的大小被调整了。
1.如果在用户向上滚动时打印新文本,则文本区域应停留在当前滚动位置,除非用户已经在文本区域的底部,在这种情况下,它应自动向下滚动以显示新内容。
我已经尝试过几种方法,包括yview()see(tk.END)方法,但我无法得到所需的行为。
下面是我的代码的简化版本:

import tkinter as tk
import tkinter.font as tkFont
from tkinter import scrolledtext

class TerminalApp:
    def __init__(self, root_):
        self.user_input_var = None
        self.root = root_
        self.root.title("Terminal-like Library")
        self.font = tkFont.Font(family="Courier", size=12)
        self.text_area = scrolledtext.ScrolledText(self.root, wrap=tk.WORD, font=self.font)
        self.text_area.pack(expand=True, fill="both")

        # Create the edit menu
        self.edit_menu = tk.Menu(self.root, tearoff=0)
        self.edit_menu.add_command(label="Copy", command=self.copy_text, accelerator="Ctrl+Shift+C")
        self.edit_menu.add_command(label="Paste", command=self.paste_text, accelerator="Ctrl+Shift+V")

        # Bind right-click event to show the edit menu
        self.text_area.bind("<Button-3>", self.show_edit_menu)

        # Bind keyboard shortcuts
        self.root.bind("<Control-Shift-c>", self.copy_text)
        self.root.bind("<Control-Shift-v>", self.paste_text)
        self.text_area.bind("<Control-Shift-C>", self.copy_text)
        self.text_area.bind("<Control-Shift-V>", self.paste_text)

    def write_output(self, output):
        yview = self.text_area.yview()[1]
        self.text_area.configure(state="normal")
        self.text_area.insert(tk.END, output)
        self.text_area.configure(state="disabled")
        if yview == 1.0:
            self.text_area.see(tk.END)

    def stop(self):
        self.root.destroy()

    def print(self, *args, **kwargs):
        text = "".join(map(str, args))
        end = kwargs.get("end", "\n")
        self.write_output(text + end)

    def show_edit_menu(self, event):
        self.edit_menu.post(event.x_root, event.y_root)

    def copy_text(self, event=None):
        selected_text = self.text_area.get(tk.SEL_FIRST, tk.SEL_LAST)
        if selected_text:
            self.root.clipboard_clear()
            self.root.clipboard_append(selected_text)

    def paste_text(self, event=None):
        clipboard_text = self.root.clipboard_get()
        if clipboard_text:
            self.text_area.insert(tk.INSERT, clipboard_text)

    def input(self, prompt=""):
        def adjust_entry_width(event):
            self.root.update_idletasks()
            entry.configure(width=(self.text_area.winfo_width() - entry.winfo_x() - 18) // self.font.measure("0"),
                            borderwidth=0, selectborderwidth=0, insertborderwidth=0, border=0.0)

        # Update the entry width when the scrolledtext widget is resized
        self.text_area.bind("<Configure>", adjust_entry_width)

        def on_enter(event=None):
            self.print(self.user_input_var.get())
            entry.destroy()

        if prompt:
            self.print(prompt, end="")
        yview = self.text_area.yview()[1]
        self.user_input_var = tk.StringVar()
        entry = tk.Entry(self.text_area, textvariable=self.user_input_var, font=self.font, relief=tk.FLAT,
                         borderwidth=0, border=0.0, insertborderwidth=0, selectborderwidth=0)
        entry.pack(side=tk.LEFT, fill="x", padx=0, pady=0, ipady=0, ipadx=0, expand=True)
        self.text_area.window_create(tk.INSERT, padx=0, pady=0, stretch=1, window=entry)
        if yview == 1.0:
            self.text_area.see(tk.END)
        adjust_entry_width(None)

        entry.bind("<Return>", on_enter)
        entry.focus_set()

        self.root.wait_window(entry)  # Wait for the entry window to close

        user_input_ = self.user_input_var.get()
        return user_input_

if __name__ == "__main__":
    root = tk.Tk()
    app = TerminalApp(root)

    app.print("Hello, world!", end=" ")
    app.print("This terminal-like library is great.")
    app.print("""
    Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur eu velit nec tortor pharetra pretium ac 
    nec felis. Nullam faucibus libero nec dui auctor pellentesque. Suspendisse eu ipsum in nibh auctor maximus vitae 
    vitae urna. Phasellus facilisis nulla tempus erat bibendum faucibus. Fusce turpis leo, condimentum eu mollis ac, 
    sodales at justo. Pellentesque dapibus feugiat leo, ac euismod lectus fermentum malesuada. Aliquam a turpis 
    tempus, vulputate risus id, lobortis ipsum. Nunc tincidunt nibh a urna suscipit egestas. Phasellus tellus tellus, 
    bibendum in lacus non, mattis pharetra arcu. Nam a est eu odio facilisis sollicitudin sit amet fringilla nunc. 
    Nunc massa enim, tempor vel est et, eleifend vulputate est. Suspendisse eu libero vel odio commodo malesuada. 
    Fusce gravida, sem ut gravida congue, magna massa accumsan libero, ac interdum arcu sem sit amet diam. Cras eu 
    eros fringilla urna fringilla faucibus quis sed dui.""".replace("\n", "").replace("    ", ""))
    app.print("""
    Quisque vitae quam neque. Maecenas tempus dapibus turpis, a laoreet velit commodo id. Vivamus id lectus turpis. 
    Nulla suscipit, augue quis egestas pellentesque, nulla sem imperdiet turpis, ut porta nulla nisl in eros. 
    Phasellus convallis dignissim mollis. Fusce ut blandit ipsum. Etiam venenatis turpis et lobortis efficitur. Morbi 
    venenatis ante nisl, sed aliquet eros tincidunt nec. Sed vitae odio ut dui accumsan dictum. Vestibulum ante ipsum 
    primis in faucibus orci luctus et ultrices posuere cubilia curae; Nulla et nulla lectus. Vivamus euismod quam ut 
    lacus varius, id sodales neque semper. Duis eleifend fermentum nisl, a commodo ex. Donec id massa erat. Vivamus 
    sed ultrices nunc.""".replace("\n", "").replace("    ", ""))
    app.print("""
    Ut molestie purus id elit porta, nec ultricies lorem pharetra. Donec nec rhoncus magna. Praesent tristique lacus 
    et metus vulputate interdum. Phasellus eu lectus quam. Sed ac diam faucibus, commodo quam eget, aliquam tortor. 
    Integer rutrum consequat lorem. Sed metus eros, placerat sit amet tempor vitae, ullamcorper non risus. Sed a 
    aliquam neque, vitae rhoncus ipsum. Nam libero purus, mattis ut scelerisque vitae, mattis at lacus. Vivamus 
    fermentum diam faucibus justo lobortis cursus. Aliquam erat volutpat. Aliquam ac vestibulum mi, 
    sit amet condimentum neque. Phasellus eget nisi et lorem ultricies finibus venenatis at metus.""".replace("\n",
                                                                                                              "").replace(
        "    ", ""))
    app.print("""
    Phasellus est leo, finibus non pretium sit amet, tempus id lacus. In hac habitasse platea dictumst. Etiam sed 
    lacinia nunc, sit amet dignissim eros. Fusce semper fermentum erat ut accumsan. Donec volutpat eget magna in 
    porttitor. In sed ultrices nisi, hendrerit ornare ante. Fusce imperdiet metus sit amet dolor bibendum 
    consectetur. Sed dapibus enim libero, id blandit augue sollicitudin id. Fusce non metus feugiat, accumsan velit 
    et, consectetur quam.""".replace("\n", "").replace("    ", ""))
    app.print("""
    Curabitur placerat placerat elit eu interdum. Proin tempus mi semper urna tempor, nec molestie metus vehicula. 
    Donec metus ante, rutrum ac porta aliquet, bibendum ut mi. Phasellus mattis erat mauris, et aliquet nisl faucibus 
    maximus. Nam ac ex turpis. Morbi pulvinar non leo in efficitur. Vivamus dignissim porttitor turpis non tempor. 
    Morbi enim neque, facilisis id odio id, iaculis finibus dolor. In consectetur urna et vulputate rutrum. Aliquam 
    eget maximus mauris. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis 
    egestas. Pellentesque viverra risus eget maximus molestie. Curabitur pulvinar dui nec mi vehicula, id vestibulum 
    urna consequat.""".replace("\n", "").replace("    ", ""))
    user_input = app.input("Enter a number: ")
    app.print(f"Entered input: {user_input}")

    root.mainloop()

字符串
我感谢任何指导或建议,以帮助我实现预期的滚动行为。提前感谢您的帮助!

wtzytmuj

wtzytmuj1#

我是这样做的:

def write_output(self, output):
    self.root.update_idletasks()
    yview = round(self.text_area.yview()[1], 1)
    index = float(self.text_area.index(tk.CURRENT)) // (self.text_area.winfo_height() // self.font.measure("0"))
    self.text_area.configure(state="normal")
    self.text_area.insert(tk.END, output)
    self.text_area.configure(state="disabled")
    if yview < index or yview == 1.0:
        self.text_area.see(tk.END)

字符串
input也是如此。“
我用以下方法解决了窗口大小调整问题:

def __init__(self, root_):
   # ...
   self.yview = None
   self.index = None
   # ...
   self.text_area.bind("<Configure>", self.adjust_on_configure)

def adjust_on_configure(self, event):
    # ...
    if not self.yview and not self.index:
        self.root.update_idletasks()
        self.yview = round(self.text_area.yview()[1], 1)
        self.index = float(self.text_area.index(tk.CURRENT)) // (
                    self.text_area.winfo_height() // self.font.measure("0"))
    if self.yview < self.index or self.yview == 1.0:
        self.text_area.see(tk.END)
    self.yview = None
    self.index = None

相关问题