password text field for customtkinter
Asked Answered
C

1

0

I am trying to integrate a python code I found here on SO by a user @srccircumflex, his code is for tkinter and I tried to modify it because I'm using customtkinter

His code was using class PassEntry(Entry): so I replaced it with class PassEntry(ctk.CTkEntry): also inside the class I replaced Entry with ctk.CTkEntry

I am a beginner in python and below is what I have so far

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)
        self.bind("<Button>", self._run)

        self._external: bool = False

        self.get = self.getpass

        if platform == "linux":
            # (
            # MultyKeys,                    ^ ` ọ ˇ
            # NoModifier,                   a b c d
            # Shift+Key,                    A B C D
            # AltGr+Key(AT-Layout),         @ ł | ~
            # AltGr+Shift+Key(AT-Layout)    Ω Ł ÷ ⅜
            # )
            self._states = (0, 16, 17, 144, 145)
        elif platform == "win32":
            # (
            # AltGr+Key(AT-Layout),         @ \ | }
            # NoModifier,                   a b c d
            # Shift+Key,                    A B C D
            # )
            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:  # windows stuff
            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 _get(self):
        return self.tk.call(self._w, 'get')

    def _delete(self, first, last=None):
        self.tk.call(self._w, 'delete', first, last)

    def _insert(self, index, string: str) -> None:
        self.tk.call(self._w, 'insert', index, string)

    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
        self.tk.call(self._w, 'insert', index, string)

    def delete(self, first, last=None) -> None:
        self._external = True
        self.tk.call(self._w, 'delete', first, last)

    def clear(self):
        del self._password
        self._password = ""
        self._delete(0, END)

    def getpass(self):
        password = self._password
        if self.getpass_range:
            assert len(self._password) in self.getpass_range, f'## Password not in {self.getpass_range}'
        if self.getpass_call:
            password = self.getpass_call.__call__(self._password)
        if self.getpass_del:
            del self._password
            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()
Ciborium answered 6/8, 2023 at 6:47 Comment(3)
ctk.CTkEntry.__init__(self, master, tk_kwargs) should be ctk.CTkEntry.__init__(self, master, **tk_kwargs) instead.Molliemollify
@Molliemollify thanks, the window now appears but it still errors out when i type something in the textbox,I updated the error shownCiborium
Note that CTkEntry does not inherit from Entry directly, so something works for Entry may not work.Molliemollify
C
0

update 2: just found out you can just use the - show="*" - inside the ctkentry instead without using the class passentry

#Create user input entry for optional password
    entry_password = ctk.CTkEntry(
                root,
                width=200,
                height=40,
                border_width=1,
                fg_color="white",
                placeholder_text="OPTIONAL: Enter password...",
                show="*",
                text_color="black",
                font=('Arial Rounded MT Bold', 14))
    entry_password.grid(row=2, column=0, padx=20, pady=(10,20))
Ciborium answered 7/8, 2023 at 12:53 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.