You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于CustomTkinter的延迟隐藏密码输入框集成求助

适配CustomTkinter的延迟隐藏密码输入框问题解决

我是Python初学者,尝试将Stack Overflow用户@srccircumflex的Tkinter延迟隐藏密码输入框代码适配到CustomTkinter中,已将原类继承的Entry替换为ctk.CTkEntry,以下是修改后的代码,希望得到技术帮助。

import customtkinter as ctk
import tkinter as tk #using messagebox
from tkinter import filedialog
from tkinter import END, INSERT, SEL_FIRST, SEL_LAST
import PyPDF2
from PyPDF2 import PdfWriter

from sys import platform
from typing import Iterable

merger = PdfWriter()

def merge_pdfs():
    #code to merge pdf files
    file_password = entry_password.getpass()
    ...

#PASSENTRY # Copyright (c) 2022 Adrian F. Hoefflin [srccircumflex] ------------------------------
class PassEntry(ctk.CTkEntry):

    def __init__(self,
                 master,
                 show: chr = "*",
                 delay: int = 800,
                 getpass_range: Iterable = None,
                 getpass_call: ... = None,
                 getpass_del: bool = False,
                 **tk_kwargs,
                 ):

        """
        Password entry with delayed hiding. Alternative to `Entry(master, show="*")'.
        Supports all common character sets(1aA!), multy keys(^`˝) and under Linux also the alternative graphics(↑Ωł).

        {Deletes the input from the widget and writes it casually into a variable. Markings and the position
        of the cursor is respected.}


        howto get the password:
            - by protected member self._password
            - by calling self.getpass (args `getpass_*' executed here)
            - by calling self.get (args `getpass_*' executed here)


        :param master: root tk
        :param show: displayed char
        :param delay: hiding delay
        :param getpass_range: check password length
        :param getpass_call: callable, gets `self._password' as argument
        :param getpass_del: delete `self._password' and flush entry if True
        :param tk_kwargs: Valid resource names: background, bd, bg, borderwidth, cursor, exportselection, fg, font, foreground, highlightbackground, highlightcolor, highlightthickness, insertbackground, insertborderwidth, insertofftime, insertontime, insertwidth, invalidcommand, invcmd, justify, relief, selectbackground, selectborderwidth, selectforeground, state, takefocus, textvariable, validate, validatecommand, vcmd, width, xscrollcommand
        """

        self._password: str = ""

        self.delay: int = delay
        self.show: chr = show

        self.getpass_range: Iterable = getpass_range
        self.getpass_call: ... = getpass_call
        self.getpass_del: bool = getpass_del

        ctk.CTkEntry.__init__(self, master, **tk_kwargs)
        self.bind("<Key>", self._run, add='+')
        self.bind("<Button>", self._run, add='+')

        self._external: bool = False

        self.get = self.getpass

        if platform == "linux":
            self._states = (0, 16, 17, 144, 145)
        elif platform == "win32":
            self._states = (0, 8, 9)

    def _char(self, event) -> str:
        def del_mkey():
            i = self.index(INSERT)
            self.delete(i - 1, i)

        if event.keysym in ('Delete', 'BackSpace'):
            return ""
        elif event.keysym == "Multi_key" and len(event.char) == 2:
            if event.char[0] == event.char[1]:
                self.after(10, del_mkey)
                return event.char[0]
            return event.char
        elif event.char != '\\' and '\\' in f"{event.char=}":
            return ""
        elif event.num in (1, 2, 3):
            return ""
        elif event.state in self._states:
            return event.char
        return ""

    def _run(self, event):

        if self._external and self._char(event):
            self._external = False
            self.clear()

        def hide(index: int, lchar: int):
            i = self.index(INSERT)
            for j in range(lchar):
                self.delete(index + j, index + 1 + j)
                self.insert(index + j, self.show)
            self.icursor(i)

        if event.keysym == 'Delete':
            if self.select_present():
                start = self.index(SEL_FIRST)
                end = self.index(SEL_LAST)
            else:
                start = self.index(INSERT)
                end = start + 1

            self._password = self._password[:start] + self._password[end:]

        elif event.keysym == 'BackSpace':
            if self.select_present():
                start = self.index(SEL_FIRST)
                end = self.index(SEL_LAST)
            else:
                if not (start := self.index(INSERT)):
                    return
                end = start
                start -= 1

            self._password = self._password[:start] + self._password[end:]

        elif char := self._char(event):
            if self.select_present():
                start = self.index(SEL_FIRST)
                end = self.index(SEL_LAST)
            else:
                start = self.index(INSERT)
                end = start

            self._password = self._password[:start] + char + self._password[end:]

            self.after(self.delay, hide, start, len(char))

    def insert(self, index, string: str) -> None:
        self._external = True
        super().insert(index, string)

    def delete(self, first, last=None) -> None:
        self._external = True
        super().delete(first, last)

    def clear(self):
        self._password = ""
        self.delete(0, END)
        # 恢复placeholder_text(如果需要)
        if self.cget("placeholder_text"):
            self.configure(placeholder_text=self.cget("placeholder_text"))

    def getpass(self):
        password = self._password
        if self.getpass_range:
            assert len(self._password) in self.getpass_range, f'## Password length not in {self.getpass_range}'
        if self.getpass_call:
            password = self.getpass_call(self._password)
        if self.getpass_del:
            self._password = ""
            self.delete(0, END)
        return password
#PASSENTRY----------------------------------------------------------------------------------

if __name__ == "__main__":

    #main window
    root = ctk.CTk()
    root.title("Merge PDF")

    #Create user input entry for filename
    ...

    #Create user input entry for optional password
    entry_password = PassEntry(
                master=root,
                width=200,
                height=40,
                border_width=1,
                fg_color="white",
                placeholder_text="OPTIONAL: Enter password...",
                text_color="black",
                font=('Arial Rounded MT Bold', 14))
    entry_password.grid(row=2, column=0, padx=20, pady=(10,20))

    # Create a button to merge the selected PDF files
    ...

    root.mainloop()

关键修改说明

  • 事件绑定追加:在bind()方法中添加add='+',避免覆盖CTkEntry原有事件处理逻辑
  • 替换底层调用:删除自定义的_get()、_delete()、_insert()方法,直接使用CTkEntry继承的公开方法
  • 简化clear方法:去掉不必要的del self._password,直接重置字符串并清空输入框,可选恢复placeholder_text
  • 优化方法调用:简化getpass_call的调用方式,去掉冗余的__call__显式调用
  • 明确错误提示:修改断言信息,明确提示密码长度不符合要求

内容的提问来源于stack exchange,提问作者k1dr0ck

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.14 05:40:54