繁体   English   中英

为什么我的小部件重叠?

[英]Why is my widget being overlapped?

我在Tkinter使用Bryan Oakley的代码向文本小部件添加行号以创建带行号的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吗?

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

首先,我不确定我是否理解函数showhide的逻辑,但是在下面的代码中,正如您将看到的,我已经改变了一些函数。

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

我最初做的基本上是让LinedText继承自Frame而不是CustomText ,并在LinedText类中创建一个名为self.text的实例变量,类型为CustomText 我这样做是因为我认为这个LinedText类是另外两个CustomTextTextLineNumbers类型对象的容器。

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

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

我还改变了一点,使TextLineNumber看起来很漂亮,就是绘制数字的地方。 检查代码的注释。

我想说的另一件事是你应该使用is或者is not来比较object的值和None ,而不是==!=

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

如果您在引入或删除窗口小部件时窗口缩小这一事实时遇到问题,请查看此问题

如何阻止Tkinter Frame缩小以适应其内容?

我希望你理解方法的逻辑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()

暂无
暂无

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

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