【问题标题】:Why is my widget being overlapped?为什么我的小部件重叠?
【发布时间】:2015-07-17 22:26:23
【问题描述】:

我正在使用 Bryan Oakley 在 Tkinter adding line number to text widget 的代码创建带有行号的 Text 小部件。但我想创建一个自定义小部件,我可以像Text 小部件一样使用它,但随后带有可选的行号。喜欢在:

t = LinedText(top)
t.insert("insert", "Hello")
t.show()

但到目前为止,当我显示行号时,它会覆盖文本小部件。窗口会自动调整大小。为什么会这样?我的代码:

import tkinter as tk


class TextLineNumbers(tk.Canvas):

    def __init__(self, *args, **kwargs):
        tk.Canvas.__init__(self, *args, **kwargs)
        self.textwidget = None

    def attach(self, text_widget):
        self.textwidget = text_widget

    def redraw(self, *args):
        '''redraw line numbers'''
        self.delete("all")

        i = self.textwidget.index("@0,0")
        while True :
            dline= self.textwidget.dlineinfo(i)
            if dline is None: break
            y = dline[1]
            linenum = str(i).split(".")[0]
            self.create_text(2,y,anchor="nw", text=linenum)
            i = self.textwidget.index("%s+1line" % i)


class CustomText(tk.Text):
    def __init__(self, *args, **kwargs):
        tk.Text.__init__(self, *args, **kwargs)

        self.tk.eval('''
            proc widget_proxy {widget widget_command args} {

                # call the real tk widget command with the real args
                set result [uplevel [linsert $args 0 $widget_command]]

                # generate the event for certain types of commands
                if {([lindex $args 0] in {insert replace delete}) ||
                    ([lrange $args 0 2] == {mark set insert}) || 
                    ([lrange $args 0 1] == {xview moveto}) ||
                    ([lrange $args 0 1] == {xview scroll}) ||
                    ([lrange $args 0 1] == {yview moveto}) ||
                    ([lrange $args 0 1] == {yview scroll})} {

                    event generate  $widget <<Change>> -when tail
                }

                # return the result from the real widget command
                return $result
            }
            ''')
        self.tk.eval('''
            rename {widget} _{widget}
            interp alias {{}} ::{widget} {{}} widget_proxy {widget} _{widget}
        '''.format(widget=str(self)))


class LinedText(CustomText):

    def __init__(self, *args, **kwargs):
        CustomText.__init__(self, *args, **kwargs)

        self.settings = self.Settings()
        self.linenumbers = None

        self.text = super()      

        self.vsb = tk.Scrollbar(orient="vertical", command=self.yview)
        self.vsb.pack(side="right", fill="y")

        self.text.configure(yscrollcommand=self.vsb.set)
        self.text.tag_configure("bigfont", font=("Helvetica", "24", "bold"))
        self.text.pack(side="left", fill="both", expand=True)
        self.text.bind("<<Change>>", self._on_change)
        self.text.bind("<Configure>", self._on_change)
        self.text.insert("end", "one\ntwo\nthree\n")
        self.text.insert("end", "four\n",("bigfont",))
        self.text.insert("end", "five\n")
        self.text.pack(side="left")

    def hide(self,event=None):
        if not self.settings.hide_linenumbers:
            self.settings.hide_linenumbers = True
            self.linenumbers.pack_forget()

    def show(self,event=None):
        if self.linenumbers == None:
            self.linenumbers = TextLineNumbers(self, width=30)
            self.linenumbers.attach(self.text)
            self.linenumbers.pack(side="left", fill="y")
        elif self.settings.hide_linenumbers:
            self.settings.hide_linenumbers = False
            self.linenumbers.pack(side="left", fill="y")

    def _on_change(self, event):
        if self.linenumbers:
            self.linenumbers.redraw()

    class Settings():
        def __init__(self):
            self.hide_linenumbers = True


if __name__ == "__main__":
    root = tk.Tk()
    text = LinedText(root)
    #text.pack(side="right", fill="both", expand=True)
    button = tk.Button(root, text="Hide", command=text.hide)
    button.pack()
    button = tk.Button(root, text="Show", command=text.show)
    button.pack()
    root.mainloop()

另外,我假设使用.pack() 而不是.pack(side="left"),小部件将绘制在以前的小部件下方。我的按钮被向右绘制。如何让他们在文本和线条小部件下方绘制?我是否绝对需要使用.grid()Frame

【问题讨论】:

    标签: python tkinter tkinter-canvas


    【解决方案1】:

    它们重叠的原因是您使行号小部件成为文本小部件的子级。您需要使用作为文本小部件和行号小部件的父级的框架,然后将它们并排打包或网格化。这正是您复制的原始代码的工作方式。

    【讨论】:

    • 我正在尝试创建类似ScrolledText 的东西。它还使用FrameScrollbarText 放入其中,但它继承自Text 并具有Frame 作为属性。当我这样做时,行号没有得到更新。必须与 ScrolledText 和 CustomText 相互破坏的两个黑客有关。但也许我必须为此提出一个新问题。
    【解决方案2】:

    首先,我不确定我是否理解函数showhide 中的逻辑,但是在下面的代码中,正如您所看到的,我对这些函数进行了一些更改。

    我也不确定你为什么在 LinedText 类中使用 super() 来初始化 self.text,但我认为问题正是从那里开始的。

    我首先做的基本上是让LinedTextFrame 继承而不是从CustomText,并在LinedText 类中创建一个名为self.text 类型为CustomText 的实例变量。我这样做是因为我认为这个LinedText 类是CustomTextTextLineNumbers 类型的其他2 个对象的容器。

    我还决定将主窗口(@98​​7654336@)分成两帧,一帧用于LinedText 对象,另一帧包含按钮ShowHide。通过这种方式,我可以打包框架,而不是单个小部件,例如,我可以为底部的按钮打包框架,在顶部打包 LinedText 对象的框架。

    在创建布局时,使用Frames 组织数据通常是正确且更简单的方法。

    为了让TextLineNumber 看起来更漂亮,我还更改了一件事,那就是绘制数字的位置。检查代码上的cmets。

    我想说的另一件事是,您应该使用isis not 将对象的值与None 进行比较,而不是使用==!=

    另外,我没有看到仅使用实例变量来创建一个类 (Settings) 的意义,这没有意义,我猜你这样做是因为这个类将来会变得更大。

    如果您对引入或移除小部件时窗口缩小这一事实有疑问,请查看此问题

    How to stop Tkinter Frame from shrinking to fit its contents?

    希望你理解showhide这两个方法的逻辑,如果不明白,就问吧。

    这是完整的代码:

    import tkinter as tk
    
    
    class TextLineNumbers(tk.Canvas):
    
        def __init__(self, *args, **kwargs):
            tk.Canvas.__init__(self, *args, **kwargs)
            self.textwidget = None
    
        def attach(self, text_widget):
            self.textwidget = text_widget
    
        def redraw(self, *args):
            '''redraw line numbers'''
            self.delete("all")
    
            i = self.textwidget.index("@0,0")
    
            while True :
                dline= self.textwidget.dlineinfo(i)
                if dline is None: break
                y = dline[1]
                linenum = str(i).split(".")[0]
    
                # changed where text is draw: it starts from 4
                self.create_text(4, y, anchor="nw", text=linenum)  
                i = self.textwidget.index("%s+1line" % i)
    
    
    class CustomText(tk.Text):
        def __init__(self, *args, **kwargs):
            tk.Text.__init__(self, *args, **kwargs)
    
            self.tk.eval('''
                proc widget_proxy {widget widget_command args} {
    
                    # call the real tk widget command with the real args
                    set result [uplevel [linsert $args 0 $widget_command]]
    
                    # generate the event for certain types of commands
                    if {([lindex $args 0] in {insert replace delete}) ||
                        ([lrange $args 0 2] == {mark set insert}) || 
                        ([lrange $args 0 1] == {xview moveto}) ||
                        ([lrange $args 0 1] == {xview scroll}) ||
                        ([lrange $args 0 1] == {yview moveto}) ||
                        ([lrange $args 0 1] == {yview scroll})} {
    
                        event generate  $widget <<Change>> -when tail
                    }
    
                    # return the result from the real widget command
                    return $result
                }
                ''')
            self.tk.eval('''
                rename {widget} _{widget}
                interp alias {{}} ::{widget} {{}} widget_proxy {widget} _{widget}
            '''.format(widget=str(self)))
    
    
    class LinedText(tk.Frame):
    
        def __init__(self, *args, **kwargs):
            tk.Frame.__init__(self, *args, **kwargs)
    
            self.settings = self.Settings()
            self.linenumbers = None
    
            self.text = CustomText(self)
            self.vsb = tk.Scrollbar(orient="vertical", command=self.text.yview)
            self.vsb.pack(side="right", fill="y")
    
            self.text.configure(yscrollcommand=self.vsb.set)
            self.text.tag_configure("bigfont", font=("Helvetica", "24", "bold"))
            self.text.bind("<<Change>>", self._on_change)
            self.text.bind("<Configure>", self._on_change)
            self.text.insert("end", "one\ntwo\nthree\n")
            self.text.insert("end", "four\n",("bigfont",))
            self.text.insert("end", "five\n")
            self.text.focus()        
            self.text.pack(side="right", fill="both", expand=True)
    
        def hide(self,event=None):
            if not self.settings.hide_linenumbers:
                self.settings.hide_linenumbers = True
                self.linenumbers.pack_forget()
                self.linenumbers = None
    
        def show(self,event=None):
            if self.linenumbers is None:
                self.linenumbers = TextLineNumbers(self, width=30)
                self.linenumbers.attach(self.text)
                self.linenumbers.pack(side="left", fill="y")
                self.settings.hide_linenumbers = False
    
        def _on_change(self, event):
            if self.linenumbers:
                self.linenumbers.redraw()
    
        class Settings():
            def __init__(self):
                self.hide_linenumbers = True
    
    
    if __name__ == "__main__":
        root = tk.Tk()
    
        top_frame = tk.Frame(root)
        text = LinedText(top_frame)
        text.pack(expand=1, fill="both")
        top_frame.pack(side="top", expand=1, fill="both")
    
        bottom_frame = tk.Frame(root)
        button = tk.Button(bottom_frame, text="Hide", command=text.hide)
        button.pack(side="right")
        button = tk.Button(bottom_frame, text="Show", command=text.show)
        button.pack(side="right")
        bottom_frame.pack(side="bottom", fill="x")
    
        root.mainloop()
    

    【讨论】:

    • 我从Text 继承,因为我想直接从我的LinedText 调用Text 方法。现在我必须做self.text.insert()。如果我从Text 继承,我可以做self.insert()。也许我可以将框架放置为属性/属性/self.frame。我正在使用super 获取CustomText 的实例,以便我可以打包它。
    • @SureshSubedi 老实说,我从未见过像您的示例中那样使用super。通常,您只需使用 self. 来引用当前实例...无论如何,我知道少打字会更容易,但在这种情况下,我认为我构造类的方式更好,但您决定。
    猜你喜欢
    • 1970-01-01
    • 2017-05-21
    • 2012-10-18
    • 1970-01-01
    • 2018-01-05
    • 1970-01-01
    • 1970-01-01
    • 2021-09-09
    • 1970-01-01
    相关资源
    最近更新 更多