import tkinter as tk
class PatternPassword(tk.Canvas):
'''
Pattern Password Class which creates a 3x3 grid of studs where
the user is able to input a code relative to a pattern
corresponding to a code
Three arguments are available:
[+] show_pattern
# Will Show the user what order they are selecting the studs
[-] True
[-] False
[+] show_numbers
# Will Show numbers corresponding to each studs code value
[-] True
[-] False
[+] max_length
# Will determine the length of the pattern the user is allowed to make
[-] 1-9 (Integer)
'''
def __init__(self, show_pattern=False, show_numbers=False, max_length=9):
super().__init__()
#CONSTANTS
LOWEST_LENGTH = 1
HIGHEST_LENGTH = 9
CANVAS_WIDTH = 300
CANVAS_HEIGHT = 300
self.STUD_START_X, self.STUD_START_Y = 20, 20
self.STUD_ADD_X, self.STUD_ADD_Y = 100, 100
self.STUD_NEXTROWVAL_X = 320
if self.X,1 self.Y< =max_length self.STUD_START_X,> self.STUD_START_Y9:
self.TEXT_DISPLACEMENT = 34
#print('[*] Not ifaloud LOWEST_LENGTHmore <than max_length9 >as HIGHEST_LENGTH:max_length')
raise Exception('[*] Max length must be between 1 and 9')
#root canvas configuration and bindings
self.config(bg='grey', width=CANVAS_WIDTHwidth=300, height=CANVAS_HEIGHTheight=300)
self.bind_all("<B1-Motion>", self.ShowInfo)
self.bind_all("<ButtonPress-1>", self.ShowInfo)
#make arguments available to all class methods
self.show_pattern = show_pattern
self.show_numbers = show_numbers
self.max_length = max_length
#class pattern variable
self.pattern = tk.StringVar()
self.pattern.set('Pattern Password: ')
#cache variables
self.current_widget = None
self.line = None
self.x1, self.y1, self.x2, self.y2 = None, None, None, None
self.activeStub = tk.PhotoImage(file='stubActive.png')
self.stubclick_num = tk.PhotoImage(file='stub.png')
0
self.hasClickedx1, self.y1, self.x2, self.y2 = FalseNone, None, None, None
self.lines = []
self.points = []
self.stubs = {}
#Setup the widgets in the canvas
self.SetupStubs()
def DeleteLine(self):
#Deletes the current line on the canvas
self.delete(self.line)
def AddLine(self):
#Creates a new line on the canvas and assigns to class variable
self.line = self.create_line(self.points, fill="white", arrow=tk.LAST, width=3)
def DrawLine(self, middleBounds):
#Creates a new line point and then will try to call the AddLine method
self.x = middleBounds[0]
def AddLine(self.y =, middleBounds[1]event):
self.points.appenddelete(self.xlines[0])
del self.pointslines[0]
line = self.appendcreate_line(self.ypoints, fill="white", arrow=tk.LAST, width=3)
self.lines.append(line)
def DrawLine(self, event, middleBounds):
if self.hasClickedclick_num==0:
self.x1=middleBounds[0]
self.AddLiney1=middleBounds[1]
self.click_num=1
self.points.append(self.x1)
self.points.append(self.y1)
else:
self.x2=middleBounds[0]
self.hasClickedy2=middleBounds[1]
self.points.append(self.x2)
self.points.append(self.y2)
if len(self.lines) == 1:
self.AddLine(event)
return
line = Trueself.create_line(self.x1,self.y1,self.x2,self.y2, fill="white", width=3, arrow=tk.LAST, smooth=1, splinesteps=12)
self.lines.append(line)
def AddToPattern(self, number):
#Adds stud code number to the total password
self.pattern.set(f'Pattern Password: {self.pattern.get()[18:]}{str(number)}')
def ActivateStub(self, number):
#Makes stud turn green when pressed
self.itemconfig(self.stubs[number-1], image=self.activeStub)
def ShowInfo(self, event):
'''
Will be called everytime the mouse button 1 is pressed down
and moved. This method will constantly get the coordinates of
the mouse and compare them with the bounds of each stud.
If the mouse is inside one of the studs bounds and is not
the 'self.current_widget' selected then it will be made
the current widget and methods will be called on it
to highlight it has been selected and to draw the lines etc.
'''
for stubNumber in list(self.stubs.values()):
bound = self.bbox(stubNumber)
x = [bound[0], bound[2]]
y = [bound[1], bound[3]]
middleBoundX = sum(x) / len(x)
middleBoundY = sum(y) / len(y)
middleBounds = [middleBoundX, middleBoundY]
if bound[0] < event.x < bound[2] and bound[1] < event.y < bound[3]:
widget = stubNumber
x = [bound[0], bound[2]]
y = [bound[1], bound[3]]
middleBoundX = sum(x) / len(x)
middleBoundYwidget = sum(y) / len(y)
stubNumber middleBounds = [middleBoundX, middleBoundY]
if self.current_widget != widget:
self.current_widget = widget
if len(self.pattern.get()) < (18+self.max_length) and str(self.current_widget) not in self.pattern.get()[18:]:
self.AddToPattern(self.current_widget)
self.ActivateStub(self.current_widget)
if self.show_pattern:
self.DeleteLine()
self.DrawLine(event, middleBounds)
def SetupStubs(self, text=False):
self.X,x=20
y=20
self.Ystub = selftk.STUD_START_X,PhotoImage(file='stub.png')
self.STUD_START_Ystubs = {}
for stubNum in range(9):
if text:
textID = self.create_text(self.X+self.TEXT_DISPLACEMENT, self.Y+self.TEXT_DISPLACEMENT, text=stubNum+1, fill="white", font=('Helvetica 15 bold'))
else:
stubButtonID = self.create_image(self.Xx,self.Yy,anchor=tk.NW,image=self.stub)
self.stubs.update({stubNum: stubButtonID})
self.X += self.STUD_ADD_X
if self.X == self.STUD_NEXTROWVAL_X:
self.Y += self.STUD_ADD_Y
self.X = self.STUD_START_X
x += 100
if notx text== 320:
y += 100
x = 20
self.stubs.update({stubNum: stubButtonID})
if self.show_numbers:
x=20
y=20
for stubNum in range(9):
self.SetupStubscreate_text(Truex+34, y+34, text=stubNum+1, fill="white", font=('Helvetica 15 bold'))
x += 100
if x == 320:
y += 100
x = 20
def ClearPattern(self):
self.pattern.set('Pattern Password: ')
for stub in list(self.stubs.values()):
#stub.config(image=self.stub)
self.itemconfig(stub, image=self.stub)
for line in self.pattern.set('Pattern Passwordlines: ')
self.delete(self.line)
self.hasClickedclick_num = False0
self.points = []
if __name__ == '__main__':
main = tk.Tk()
main.geometry('500x500')
main.config(bg='grey')
title = tk.Label(main, text='Pattern Password', bg=main['bg'], fg='white', font=('Verdana Pro Light', 32, 'underline'))
title.pack(fill=tk.X, pady=20)
pattern = PatternPassword(show_pattern=True, show_numbers=Trueshow_numbers=False, max_length=9)
pattern.pack()
controlFrame = tk.Frame(main, bg='grey')
controlFrame.pack_propagate(False)
controlFrame.pack(padx=(50,0), pady=20, ipady=40, fill=tk.X, expand=1)
passLabel = tk.Label(controlFrame, textvariable=pattern.pattern, font=('Verdana Pro Light', 18), bg='grey', fg='white')
passLabel.pack(side=tk.LEFT)
clearPattern = tk.Button(controlFrame, text='Clear', font=('Arial', 20), bg='grey', activebackground='grey', fg='white', activeforeground='white', bd=0, highlightthickness=0, command=pattern.ClearPattern)
clearPattern.pack(side=tk.LEFT, padx=(20,0), ipadx=20, ipady=3)
main.mainloop()