繁体   English   中英

如何通过阻止输入的最新字符来限制 python 中 tkinter 文本小部件的长度?

[英]how do you restrict the length of a tkinter text widget in python by blocking the latest character entered?

我试图以一种非常具体的方式限制 Python 中 tkinter 文本小部件或条目小部件(任一小部件类型都可以)中的字符数量。 我有一个接近文本小部件的解决方案:

string =  self.text.get("1.0","end-1c")
if len(string) >= 5:
    self.text.delete('1.0', "end-1c")
    string = string[:-1]
    self.quantity_entry.insert(END, string)

但是上面的代码做了以下事情:如果我输入 123456,它会将内容变成 12346。它将现有字段的最后一个字符 (5) 砍掉,然后将输入的最后一个字符 (6) 放在最后。 所以它变成了 12346。

我想要的是 12345 保持 12345,这意味着现有的 12345 字符串应保持原样,并且有效地阻止附加任何新字符。 有没有办法做到这一点?

我尝试使用:

self.text.configure(state="disabled")

这确实阻止了后续字符的插入,因此 12345 保持为 12345。但这会阻止将来编辑该字段。 即使随后尝试使用:

self.text.configure(state="normal")

该字段保持锁定状态。 如果用户达到五个字符的限制,我希望用户能够编辑该字段。

谢谢你。

跟踪变量将更加整洁。 然后它甚至不会在屏幕上短暂闪烁。 您可以将其打包为一个整洁的子类,如下所示:

import tkinter as tk

class Lotfi(tk.Entry):
    def __init__(self, master=None, max_len=5, **kwargs):
        self.var = tk.StringVar()
        self.max_len = max_len
        tk.Entry.__init__(self, master, textvariable=self.var, **kwargs)
        self.old_value = ''
        self.var.trace('w', self.check)

    def check(self, *args):
        if len(self.get()) <= self.max_len:
            self.old_value = self.get() # accept change
        else:
            self.var.set(self.old_value) # reject change

#demo code:
window = tk.Tk()
ent=Lotfi(window, max_len=5)
ent.pack()
window.mainloop()

我想出了解决方案。 在.bind方法中,我需要使用

"<KeyRelease>"

而不是

"<Key>"

然后,作为次要编辑,需要将> = 5更改为>5。现在可以使用。 当我输入123456时,它非常简短地在屏幕上显示123456,但是随后很快将其切掉6,所以它又回到了12345。

我知道这很旧,但认为它可以帮助一些人。

一直在寻找一个非常优雅的解决方案来解决这个问题。 但是还没有找到。 所以我想我会发布我的凌乱的。

如果你觉得这很有用,请投票,如果你对我如何更清楚地沟通有想法,我也很乐意。

这是“一个”答案。 但是,如果您解释为什么选择编写您所做的代码会有所帮助,这样我就可以更好地指导您思考得出解决方案的方法。

# IMPORT MODULES AND ALIAS THEM
import tkinter as tk

# This is a place holder to self.quantity_entry.insert(END, string) just to 
# simulate the call to some other object
def OtherTextWidget(string):
    print( f"{text1.index(tk.END)}, {string}" )

# This is the 'meat' of your solution that limits the input
def Restrict(e=None):
    print(e.type) # just to proof that we are pressing a key

    # If for some unknown reason this function gets called on key-release, it will 
    # terminate the function. Because it will break our desired behavior.
    if int(e.type) == 3: 
            return

    # The 'widget.bind()' event argument pass a reference to the widget that
    # called it. Se we use that here to get the text from the caller 
    # "text1.bind()" as event.widget.get() note we have aliased 'event' as 'e'
    # inside the function space.

    # Get text from start to the current end length minus the '\n' character
    string =  e.widget.get("1.0","end-1c")

    # If there are 5 or more characters go ahead with the limit
    # Note len() starts at 0, so 4<= is equal to 5+ chars
    if 4 <= len(string):
        # Get the first five characters before the delete
        OtherTextWidget( e.widget.get("1.0","1.5") )
    
        # !!NOTES!!
        # We are lazy and don't want to code a rejection for a 6th character. So 
        # we overwrite the 5th character, the result is the same for less work.
  
        # we are operating during the key press, and the 6th character is not 
        # inserted until the key release. The 5th character is empty if you 
        # debug it after the delete. Again the 5th character will be replaced 
        # by the 6th on the key release.
   
        # delete the 5th character to the '\n', remember we are counting from 0.
        e.widget.delete('1.4', "end-1c")

# TKINTER OBJECTS SETUP:
root = tk.Tk()
text1 = tk.Text(root, width=7, height=1) # 7 chars long, 1 char high
# We give the "entry box" 5 +2 extra character for aesthetics spacing.
# It just looks better with a little room to breath.

text1.pack() # fits the widget in the minimum space within the frame/window
text1.focus_set() # because I'm lazy and I want the cursor already in the widget.

# BIND TEXT WIDGET KEY INPUT EVENT:
# Whenever there is a key press inside the text widget this will fire the 
# Restrict() function with the 'kepress event' argument
# That events data is in key:value pairs and looks something like this:
# <KeyPress event state=Mod1 keysym=1 keycode=97 char='1' x=1959 y=69>
text1.bind('<Key>', Restrict)

# start the tkinter event loop and open a toplevel window
root.mainloop()

# The '<Key>' gives a e.type of 2 or key press/down
# if you use '<KeyRelease>' it gives a e.type of 3 release/up and we will have to 
# change some code around to make it work. But the important difference is it will 
# place the 6th character in the field then delete it, that doesn't look crisp. 
# This is why we are operating on a key press rather than release, because we want 
# to operate on the string before the character is insert into the text widget.

为了清楚起见,这里是有限的注释代码。 (更新:我认为拥有实际显示的字符串会很好,因此添加了此发布切换来捕获它。)

import tkinter as tk

def OtherTextWidget(string):
    print("Key Press Phase:", string)

# Solution - Step 1. toggle full flag
global full #(update)
full = False

# Solution - Step 4. profit
def AfterRestrict(e=None): #(update)
    global full
    if True == full:
        OtherTextWidget( e.widget.get("1.0","1.5") )
        print("Key Release Phase:", e.widget.get("1.0","1.5") )

# Solution - Step 3. limit input
def Restrict(e=None):
    global full #(update)
    string = e.widget.get("1.0","end-1c")
    if 4 <= len(string):
        e.widget.delete('1.4', "end-1c")
        full = True #(update)
    else: #(update)
        full = False

root = tk.Tk()
text1 = tk.Text(root, width=7, height=1)
text1.pack()
text1.focus_set()

# Solution - Step 2. get input event from widget
text1.bind('<Key>', Restrict)
text1.bind('<KeyRelease>', AfterRestrict) #(update)

root.mainloop()

很抱歉冗长很累,但希望这对一些人有所帮助。 :)

暂无
暂无

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

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