2

I have written a password strength checker that dynamically checks the strength of the password keyed into a ttk.Entry widget. I have applied the criteria by @ePi272314 and the tutorial on adding validation by @ByranOakley to a ttk.Entry widget. The python script for this password strength checking ttk.Entry widget is given below and it works.

Presently, I like to express method _passwordStrength() as a @staticmethod so that other classes may use it. For this to happen, I need to pass self.pwstrength, a tk.StringVar(), into it. Also, I need to include this tk.StringVar when I register the @staticmethod. However, I am having difficulty implementing this.

I tried something like:

vcmd = (self.register(lambda:App._passwordStrength(self.pwstrength),'%P')) 

but got the error msg:

Exception in Tkinter callback
Traceback (most recent call last):
  File "/usr/lib/python3.5/tkinter/__init__.py", line 1552, in __call__
    args = self.subst(*args)
TypeError: 'str' object is not callable

Appreciate guidance on how to register the @staticmethod _passwordStrength() when it contains a tk.StringVar input parameter.

Python Script:

import tkinter as tk
import tkinter.ttk as ttk
import re


class App(ttk.Frame):

    def __init__(self, parent=None, *args, **kwargs):

        ttk.Frame.__init__(self, parent, style='self.TFrame')

        self.style=ttk.Style()
        self.style.configure('self.TFrame', background='pink', borderwidth=10,
                             relief='raised')

        self.pwstrength = tk.StringVar()

        label = ttk.Label(self, text="Password:   ")

        vcmd = (self.register(self._passwordStrength), '%P')
        ePassword = ttk.Entry(self, validate="key", validatecommand=vcmd)
        warnLabel = ttk.Label(self, textvariable=self.pwstrength)

        label.grid(row=0, column=0, sticky='w', padx=20, pady=20)
        ePassword.grid(row=0, column=1, sticky='w')
        warnLabel.grid(row=1, column=1, sticky='w')      


    def _passwordStrength(self, P):
        '''Check password strength.

        A password is considered strong if:
            8 characters length or more
            1 digit or more
            1 symbol or more
            1 uppercase letter or more
            1 lowercase letter or more'''

        password = P
        print (password, len(password))

        # check length
        if password == '':
            self.pwstrength.set('')
            return True

        # check length
        if len(password) < 8:
            self.pwstrength.set('Password is too short')
            return True

        # check for digits
        if not re.search(r"\d", password):
            self.pwstrength.set('Password missing a number.')
            return True

        # check for uppercase
        if not re.search(r"[A-Z]", password):
            self.pwstrength.set('Password missing upper case letter.')
            return True

        # check for lowercase
        if not re.search(r"[a-z]", password):
            self.pwstrength.set('Password missing lower case letter.')
            return True

        # check for symbols
        if not re.search(r"\W", password):
            self.pwstrength.set('Password missing a symbol.')
            return True

        # Passed all checks.
        self.pwstrength.set('Strong password provided.')
        return True



if __name__=='__main__':
    root = tk.Tk()
    root.geometry('300x300+700+250')
    root.title('Password Strength Check')

    app=App(root)
    app.grid(row=0, column=0, sticky='nsew')

    root.rowconfigure(0, weight=1)
    root.columnconfigure(0, weight=1)

    root.mainloop()
martineau
  • 119,623
  • 25
  • 170
  • 301
Sun Bear
  • 7,594
  • 11
  • 56
  • 102

2 Answers2

1

You can do it by modifying your _passwordStrength() function to accept a tk.StringVar argument along with functools.partial to supply this to the now static function (which can't reference self.pwstrength since it's no longer a regular method with a self argument).

Here's what I mean:

import functools
import tkinter as tk
import tkinter.ttk as ttk
import re

class App(ttk.Frame):

    def __init__(self, parent=None, *args, **kwargs):

        ttk.Frame.__init__(self, parent, style='self.TFrame')

        self.style=ttk.Style()
        self.style.configure('self.TFrame', background='pink', borderwidth=10,
                             relief='raised')

        self.pwstrength = tk.StringVar()

        label = ttk.Label(self, text="Password:   ")

#        vcmd = (self.register(self._passwordStrength), '%P')
        valcommand = self.register(
                        functools.partial(App._passwordStrength, self.pwstrength)
                     )
        vcmd = (valcommand, '%P')
        ePassword = ttk.Entry(self, validate="key", validatecommand=vcmd)
        warnLabel = ttk.Label(self, textvariable=self.pwstrength)

        label.grid(row=0, column=0, sticky='w', padx=20, pady=20)
        ePassword.grid(row=0, column=1, sticky='w')
        warnLabel.grid(row=1, column=1, sticky='w')

    @staticmethod
    def _passwordStrength(svar, P):
        '''Check password strength.

        A password is considered strong if:
            8 characters length or more
            1 digit or more
            1 symbol or more
            1 uppercase letter or more
            1 lowercase letter or more'''

        password = P
        print (password, len(password))

        # check length
        if password == '':
            svar.set('')
            return True

        # check length
        if len(password) < 8:
            svar.set('Password is too short')
            return True

        # check for digits
        if not re.search(r"\d", password):
            svar.set('Password missing a number.')
            return True

        # check for uppercase
        if not re.search(r"[A-Z]", password):
            svar.set('Password missing upper case letter.')
            return True

        # check for lowercase
        if not re.search(r"[a-z]", password):
            svar.set('Password missing lower case letter.')
            return True

        # check for symbols
        if not re.search(r"\W", password):
            svar.set('Password missing a symbol.')
            return True

        # Passed all checks.
        svar.set('Strong password provided.')
        return True



if __name__=='__main__':
    root = tk.Tk()
    root.geometry('300x300+700+250')
    root.title('Password Strength Check')

    app=App(root)
    app.grid(row=0, column=0, sticky='nsew')

    root.rowconfigure(0, weight=1)
    root.columnconfigure(0, weight=1)

    root.mainloop()
martineau
  • 119,623
  • 25
  • 170
  • 301
  • I can't registering the same @staticmethod in a [switching frame scenario](https://stackoverflow.com/q/49313223/5722359). Can you advice me on the issue? Thanks. – Sun Bear Mar 16 '18 at 04:33
0

You don't need to set something as a static method for it to be used by other classes. So long as your class has all the required attributes and methods, it will work:

class X:
    def __init__(self):
        self.a = 0
    def act_on_a(self):
        self.a = self.a + 1

class Y:
    def __init__(self, a):
       self.a = a

y = Y(a=3)
X.act_on_a(y)
print(y.a)  # prints 4

In general, there is very little use for staticmethod except for namespacing. Often things I see as static methods should really be top level python functions.

FHTMitchell
  • 11,793
  • 2
  • 35
  • 47