Python Forum
Python code review | Tkinter gui application
Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Python code review | Tkinter gui application
#16
(Nov-30-2023, 10:05 PM)menator01 Wrote: Although I don't have the database and some error checking done yet. Thought I would share what I have.

Code updated. Still need to work on edit and delete. Maybe some more tweaks.
On table creation checks for any entries. If not one adds admin entry. username admin, password admin

# Do the imports
import tkinter as tk
from tkinter import ttk
from random import sample 
from string import ascii_letters, digits
import os
import sqlite3 as sq
import hashlib
from PIL import Image
 
# Get path
path = os.path.realpath(os.path.dirname(__file__))
 
# Characters for random password use
characters = ascii_letters+digits+'#&!*'
 
 
class Data:
    ''' Class will handle all database interaction '''
    def __init__(self):
        self.connect = sq.connect(f'{path}/resources/PasswordManager/password.db')
        self.cursor = self.connect.cursor()

    def create(self):
        query = '''
                    create table if not exists users (
                        id integer primary key,
                        level integer default 5,
                        website text not null,
                        username text not null,
                        password text not null,
                        date default current_timestamp
                    );
                '''

        self.connect.execute(query)
        self.connect.commit()

        # Check if we have any entries. If not create the admin entry
        query = 'select exists(select 1 from users);'

        result = self.cursor.execute(query).fetchone()
        if result[0] == 0:
            self.insert(level=1,website='None',username='admin',password='admin')

    def insert(self, level=5, **args):
        ''' Method for inserting users '''
        level = int(level)
        website = args['website']
        username = args['username']

        # Hask password
        password = args['password'].encode()
        password = hashlib.sha256(password).digest()

        query = '''
                    insert into users (level, website, username, password) values (?,?,?,?);
                '''

        self.cursor.execute(query, (level, website, username, password))
        self.connect.commit()

    def getuser(self, username, password):
        ''' Method for getting single user '''
        username = username.strip()
        password = password.strip()
        password = password.encode()
        password = hashlib.sha256(password).digest()

        query = f'select username, level, password from users where username="{username}"'

        cursor = self.cursor.execute(query).fetchone()
        if cursor:
            if cursor[2] == password:
                return cursor
        return False

    def getall(self):
        ''' Method for getting all users '''
        query = 'select * from users'
        result = self.cursor.execute(query).fetchall()
        return result
 

class Page(tk.Frame):
    ''' Base class for pages '''
    def __init__(self, *args, **kwargs):
        tk.Frame.__init__(self, *args, **kwargs)
         

class LoginForm(Page):
    ''' Page for the login view '''
    def __init__(self, *args, **kwargs):
        Page.__init__(self, *args, **kwargs)

    def form(self, parent):
        self.parent = parent
        container = tk.Frame(parent)
        container.grid(column=0, row=2, sticky = 'new', padx=5, pady=5)

        self.msg = tk.Label(container, anchor='w', padx=5, pady=5)
        self.msg.grid(column=0, columnspan=2, row=0, sticky='new', padx=(280,5), pady=1)
        self.msg.grid_forget()

        label = tk.Label(container, text='Username:')
        label.grid(column=0, row=1, sticky='new', padx=(275,5), pady=5)

        self.username = tk.Entry(container)
        self.username.focus()
        self.username.grid(column=1, row=1, sticky='new', padx=5, pady=5)

        label = tk.Label(container, text='Password:')
        label.grid(column=0, row=2, sticky='new', padx=(275,5), pady=5)

        self.password = tk.Entry(container, show='*')
        self.password.grid(column=1, row=2, sticky='new', padx=5, pady=5)

        btnframe = tk.Frame(container)
        btnframe.grid(column=0, columnspan=2, row=3, sticky='new', padx=(250,5), pady=5)

        self.btn = tk.Button(btnframe, text='Login', cursor='hand2')
        self.btn.grid(column=0, row=0, sticky='new', padx=(150,0), pady=5)

        self.cancel = tk.Button(btnframe, text='Cancel', cursor='hand2')
        self.cancel.grid(column=1, row=0, sticky='new', padx=(5,0), pady=5)


class MainView(Page):
    ''' Page for the main view '''
    def __init__(self, *args, **kwargs):
        Page.__init__(self, *args, **kwargs)
        
    def view(self, parent):
        self.container = tk.Frame(parent)
        self.container.grid(column=0, row=2, sticky='news', padx=5, pady=5)
        self.container.grid_columnconfigure(0, weight=3)
        self.container.grid_rowconfigure(0, weight=0)
        self.container.grid_rowconfigure(1, weight=3)

        self.msg = tk.Label(self.container, padx=5, pady=5)
        self.msg.grid(column=0, row=0, sticky='new', padx=5, pady=1)
        self.msg.grid_forget()

        contentframe = tk.Frame(self.container)
        contentframe.grid(column=0, row=1, sticky='news', padx=5, pady=1)
        contentframe.grid_columnconfigure(0, weight=3)
        contentframe.grid_rowconfigure(0, weight=3)

        columns = ('id', 'level', 'website', 'username', 'date')
        self.tree = ttk.Treeview(contentframe, columns=columns, show='headings')
        for column in columns:
            self.tree.heading(column, text=column.title())
            width = 5 if column in ('id', 'level') else 20
            self.tree.column(column, minwidth=5, width=width)
        self.tree.grid(column=0, row=0, sticky='news', padx=5, pady=5)

        self.linkframe = tk.Frame(self.container)
        self.linkframe.grid(column=0, row=2, sticky='new', padx=5, pady=5)
        for i in range(4):
            self.linkframe.columnconfigure(i, weight=3, uniform='links')

        self.add_label = tk.Label(self.linkframe, text='Add', fg='blue', cursor='hand2')
        self.add_label.grid(column=0, row=0, sticky='new', padx=5, pady=5)
        self.add_label.bind('<Enter>', lambda event: self.on_enter(self.add_label))
        self.add_label.bind('<Leave>', lambda event: self.on_exit(self.add_label))

        self.edit_label = tk.Label(self.linkframe, text='Edit', fg='blue', cursor='hand2')
        self.edit_label.grid(column=1, row=0, sticky='new', padx=5, pady=5)
        self.edit_label.bind('<Enter>', lambda event: self.on_enter(self.edit_label))
        self.edit_label.bind('<Leave>', lambda event: self.on_exit(self.edit_label))

        self.delete_label = tk.Label(self.linkframe, text='Delete', fg='blue', cursor='hand2')
        self.delete_label.grid(column=2, row=0, sticky='new', padx=5, pady=5)
        self.delete_label.bind('<Enter>', lambda event: self.on_enter(self.delete_label))
        self.delete_label.bind('<Leave>', lambda event: self.on_exit(self.delete_label))

        self.exit_label = tk.Label(self.linkframe, text='Quit', fg='firebrick', cursor='hand2')
        self.exit_label.grid(column=3, row=0, sticky='new', padx=5, pady=5)
        self.exit_label.bind('<Enter>', lambda event: self.on_enter(self.exit_label))
        self.exit_label.bind('<Leave>', lambda event: self.on_quit(self.exit_label))

    def on_enter(self, link):
        link['fg'] = 'red'

    def on_exit(self, link):
        link['fg'] = 'blue'

    def on_quit(self, link):
        link['fg'] = 'firebrick'


class DataForm(Page):
    ''' Page for the data form '''
    def __init__(self, *args, **kwargs):
        Page.__init__(self, *args, **kwargs)

    def view(self, parent):
        container = tk.Frame(parent, padx=200)
        container.grid(column=0, row=2, sticky='news', padx=5, pady=5)
        container.grid_columnconfigure(0, weight=1)
        container.grid_columnconfigure(1, weight=3)

        self.msg = tk.Label(container, anchor='w', padx=5, pady=5)
        self.msg.grid(column=0, columnspan=2, row=0, sticky='new', padx=5,pady=5)
        self.msg.grid_forget()

        label = tk.Label(container, text='Website:', anchor='w')
        label.grid(column=0, row=1, sticky='new', padx=5, pady=5)
        self.website = tk.Entry(container)
        self.website.grid(column=1, row=1, sticky='new', padx=5, pady=5)

        label = tk.Label(container, text='Username:', anchor='w')
        label.grid(column=0, row=2, sticky='new', padx=5, pady=5)
        self.username = tk.Entry(container)
        self.username.grid(column=1, row=2, sticky='new', padx=5, pady=5)

        label = tk.Label(container, text='Password:', anchor='w')
        label.grid(column=0, row=3, sticky='new', padx=5, pady=5)
        self.password = tk.Entry(container)
        self.password.grid(column=1, row=3, sticky='new', padx=5, pady=5)

        self.gen_btn = tk.Button(container, text='Generate Password')
        self.gen_btn.grid(column=0, row=4, sticky='new', padx=5, pady=5)

        self.add_btn = tk.Button(container, text='Add Data')
        self.add_btn.grid(column=1, row=4, sticky='new', padx=5, pady=5)


class Window(tk.Frame):
    ''' Container class '''
    def __init__(self, parent, *args, **kwargs):
        tk.Frame.__init__(self, *args, **kwargs)
        parent.columnconfigure(0, weight=1)
        parent.rowconfigure(0, weight=1)
        parent.geometry('800x600')
        parent.resizable(False, False)
        self.parent = parent

        self.container = tk.Frame(parent)
        self.container.grid(column=0, row=0, sticky='news')
        self.container.columnconfigure(0, weight=1)
        self.container.rowconfigure(1, weight=0)
        self.container.rowconfigure(2, weight=3)

        img = f'{path}/resources/PasswordManager/logo2.png'
        image = tk.PhotoImage(file=img)
        image.backup = image

        label = tk.Label(self.container, image=image)
        label.grid(column=0, row=0, sticky='new', padx=5, pady=5)


class Controller:
    ''' Controller class handles communictaions between all other classes '''
    def __init__(self, data, window):
        self.data = data
        self.window = window
        
        # Create the database table if not exists
        self.data.create()
        
        # Pages
        self.loginform = LoginForm()
        self.loginform.form(self.window.container)
        self.mainview = MainView()
        self.dataform = DataForm()

        # Button Commands
        self.loginform.btn['command'] = self.login
        self.loginform.cancel['command'] = self.window.parent.destroy

        # Binds
        self.window.parent.bind('<Return>', lambda event: self.login())
        self.window.parent.bind('<KP_Enter>', lambda event: self.login())

    def _mainview(self, msg=None):
        ''' Method for calling the main view '''
        self.mainview.view(self.window.container)
        self.mainview.add_label.bind('<Button-1>', lambda event: self.addform())
        self.mainview.exit_label.bind('<Button-1>', lambda event: self.window.parent.destroy())
        if msg:
            self.mainview.msg.config(text=msg, bg='darkgreen', fg='lime')
            self.mainview.msg.grid(column=0, row=0, sticky='new', padx=5, pady=1)
            self.window.parent.after(3000, self._mainview)
        else:
            self.mainview.msg.grid_forget()

        data = self.data.getall()
        for index, entry in enumerate(data):
            self.mainview.tree.insert('', index, values=(entry[0], entry[1], entry[2], entry[3], entry[5]))

    def addform(self):
        ''' Method for calling the add data form '''
        self.dataform.view(self.window.container)
        self.dataform.gen_btn['command'] = self.gen
        self.dataform.add_btn['command'] = self.add
        self.window.parent.bind('<Return>', lambda event: self.add())
        self.window.parent.bind('<KP_Enter>', lambda event: self.add())

    def gen(self):
        ''' Method for generating a 10 character password '''
        password = ''.join(sample(characters, k=10))
        self.dataform.password.delete(0, tk.END)
        self.dataform.password.insert(0, password)

    def add(self):
        ''' Method for adding data to database '''
        website = self.dataform.website.get().strip()
        username = self.dataform.username.get().strip()
        password = self.dataform.password.get().strip()

        error = 0
        messages = []
        self.dataform.msg['fg'] = 'red'
        if not website:
            error += 1
            messages.append('Website')
        if not username:
            error += 1
            messages.append('Username')
 
        if not password:
            error += 1
            messages.append('Password')

        if error > 0:
            msg = 'fields' if error > 1 else 'field'
            self.dataform.msg.grid(column=0, columnspan=2, row=0, sticky='new', padx=5,pady=5)
            self.dataform.msg.config(text=f'Required {msg}: {", ".join(messages)}', bg='pink', fg='red')
            
        else:
            self.data.insert(website=website, username=username, password=password)
            self.mainview.container.destroy()
            self.mainview.container = tk.Frame(self.window.container)
            self.mainview.container.grid(column=0, row=2, sticky='news', padx=5, pady=5)
            self.mainview.container.grid_columnconfigure(0, weight=3)
            self.mainview.container.grid_rowconfigure(0, weight=3)
            self._mainview('Successfully added data.')

    def login(self):
        username = self.loginform.username.get().strip()
        password = self.loginform.password.get().strip()

        if not username or not password:
            self.loginform.msg.grid(column=0, columnspan=2, row=0, sticky='new', padx=(280,5), pady=1)
            self.loginform.msg.config(text='All fields are required', fg='red', bg='pink')
        else:
            result = self.data.getuser(username, password)
            if result:
                self._mainview('You are now logged in.')
            else:
                self.loginform.msg.grid(column=0, columnspan=2, row=0, sticky='new', padx=(280,5), pady=1)
                self.loginform.msg.config(text=' That username and password\ncombination does not exist.', fg='red', bg='pink')

if __name__ == '__main__':
    root = tk.Tk()
    controller = Controller(Data(), Window(root))
    root.mainloop()

Hey, your code looks more readable and proffesional than mine but the concept is to create password manager program and in your tabke u doesnot show the passwords but each row in the data is served as credential for the login frame at the beggining the idea was to create 1 admin user so it will prevent any person to check all the password i store
maybe it is ur goal and the code is still on work and thats why the program does not fit the original idea

in addition i liked the use of database instead of simple file managment as csv or txt file i think i will use it in the next project
Reply


Messages In This Thread
RE: Python code review | Tkinter gui application - by Sr999 - Dec-01-2023, 01:17 PM

Possibly Related Threads…
Thread Author Replies Views Last Post
  GUI application - code review Sr999 3 1,063 Jan-06-2024, 10:14 PM
Last Post: Sr999
  Code review of my rock paper scissors game Milan 0 2,214 May-25-2022, 06:59 AM
Last Post: Milan
  Review on (new) Python module: Function but Lazy Python jeertmans 3 2,636 Nov-01-2021, 06:57 PM
Last Post: ndc85430
  First time python user - Calculator code review Steamy 1 2,418 Jul-22-2020, 05:59 PM
Last Post: Larz60+

Forum Jump:

User Panel Messages

Announcements
Announcement #1 8/1/2020
Announcement #2 8/2/2020
Announcement #3 8/6/2020