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
#11
my pref would be to use a database. sqlite comes to mind and is easy to manage.
I welcome all feedback.
The only dumb question, is one that doesn't get asked.
My Github
How to post code using bbtags


Reply
#12
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()
I welcome all feedback.
The only dumb question, is one that doesn't get asked.
My Github
How to post code using bbtags


Reply
#13
(Nov-29-2023, 03:12 AM)rob101 Wrote:
(Nov-28-2023, 11:41 PM)Sr999 Wrote: Again appreciate your time and I am
Waiting for your review
Thanks again

You are very welcome and while I don't want to come across as an "ass" here, it was very easy to circumvent your 'admin password', so without knowing what that password is, I can see that the fake google login password is 123123.

I'm not saying that what you've created here is not good in its own right, it's just I don't think it should be used (as it is) for any serious password management, but by putting this up on GitHub, you are kind of suggesting that it could be used in such a way: sorry to be so negative about it.

Possibly (but I don't know, because I've never tried to do this) the way this could work, is to have the .csv file stored in an encrypted .zip archive (or the like of). I do know that one can create such an archive at will, but I've no clear idea how you could have your app access and use that, but it's an idea.

Hey again sorry it took me awhile to visit the post i had alot of work and little time to program, i understand your point of view and i am sorry if it came out like im trying to suggest a correct way to build password manager but i use github as a tool to easliy upload and change file contents while share to other people who can review my code and give me notes if anyone will enter my profile and will use my code without check it first it is not my resposiblity and i never mentioned inside the git repo that my code is ready to use for serivce use, it is only a learning phase program just part of my python learning..

about the security problem i did say it was weak security solution taht i found but u made me dig up lil further and i have updated the code with this additions:
-after program loads it ill ask for admin password instantly before you can access the software itself.
-each time there password request prompt the field is replaced with bullets signs ( • )
-no longer working with saved csv file but changed it to data_file.enc and store the whole csv file as encrypted file.
-after admin password inserted the software will attempt to decrypt the file if decryption was successful the content of the file will be changed to csvString and then into pandas dataFrame
-admin verification does not use a stored passoword in file or stored password in the code but just try to decrypt the data_file each time with the current inserted admin pass
-instead of save the file each time we make a change i just override the Tk.destroy function to save the current data in the panda Dataframe into encrypted file using the password inserted at the start unless the user changed the password from the "change password" button in main menu
-added search button to search for specific details about specific website
-added edit ability if you double click any cell in the table an Entry box will popup instead of the field you are trying to edit and u can re:edit the cell as you like

*****
about the Cipher.py file - this is written file i took from the lib simpleCrypt lib after failed to install the lib itself just installed the main lib Crypto
and copy paste the simpleCrypt encrypt-decrypt function into a file i created named Cipher.py
coulndt solve why i getting errors when i try to install simpleCrypt lib
*****
eventually i needed to override Tk function so i think for the next project ill start using OOP as the roots for my program instead the mess i made here xD


please review the code it self and lemme know if the solution was fine and if not give your notes im here to learn :)
Reply
#14
(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
Reply
#15
The password cane be shown but is hashed i the database.
I welcome all feedback.
The only dumb question, is one that doesn't get asked.
My Github
How to post code using bbtags


Reply
#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
#17
(Dec-01-2023, 01:16 PM)menator01 Wrote: The password cane be shown but is hashed i the database.

maybe i didnt understand but i saw the password is inserted as sha256 into the database so how you can display the passwords and unhash it?
and i have a question: what is the "level" paramter stands for in the table
Reply
#18
From what I read I don't think the hash can't be undone. The level is for regular user permissions or admin permissions etc... It was just me playing around with different options. If it's for personal use, probably don't need the encryption. Or go for one of the other encrypt, decrypt options.
I welcome all feedback.
The only dumb question, is one that doesn't get asked.
My Github
How to post code using bbtags


Reply
#19
Altered code. I Commented out the password hash. Added row colors. Admin row will be pink, while others will alternate between a light yellow and light gray. Still not worked on the edit or delete. Maybe when I get home from work in the morning.

# 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']
        password = args['password']

        # Hash 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 = ('website', 'username', 'password', 'date')
        # columns = ('id', 'level', 'website', 'username', 'password', 'date')
        self.tree = ttk.Treeview(contentframe, columns=columns, show='headings', selectmode='browse')
        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()
        self.mainview.tree.tag_configure('odd', background='#fffeee')
        self.mainview.tree.tag_configure('even', background='#eeeeef')
        self.mainview.tree.tag_configure('admin', background='pink')
        for index, entry in enumerate(data):
            if entry[3]  == 'admin':
                self.mainview.tree.insert('', index, values=(entry[2], entry[3], entry[4], entry[5]), tags=('admin', ))
            elif index % 2 == 0:
                self.mainview.tree.insert('', index, values=(entry[2], entry[3], entry[4], entry[5]), tags=('even', ))
            else:
                self.mainview.tree.insert('', index, values=(entry[2], entry[3], entry[4], entry[5]), tags=('odd', ))
            # self.mainview.tree.insert('', index, values=(entry[0], entry[1], entry[2], entry[3], entry[4], 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()
I welcome all feedback.
The only dumb question, is one that doesn't get asked.
My Github
How to post code using bbtags


Reply
#20
The final version. May still be a bug or two.

# 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, edit=False, **args):
        ''' Method for inserting users '''
        level = int(level)
        website = args['website']
        username = args['username']
        password = args['password']

        if 'id' in args.keys():
            id = args['id']

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

        if edit:
            query = f'update users set website="{website}", username="{username}", password="{password}" where id="{id}";'
            self.cursor.execute(query)
        else:
            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

    def delete(self, id):
        if id:
            query = f'delete from users where id="{id}";'
            self.cursor.execute(query)
            return True
        return False
 

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', 'website', 'username', 'password', 'date')
        # columns = ('id', 'level', 'website', 'username', 'password', 'date')
        self.tree = ttk.Treeview(contentframe, columns=columns, show='headings', selectmode='browse')
        for column in columns:
            self.tree.heading(column, text=column.title())
            width = 50 if column in ('id', 'level') else 50
            stretch = 'no' if column == 'id' else 'yes'
            self.tree.column(column, minwidth=50, width=width, stretch=stretch)
        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, data=None, edit=False):
        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)

        if edit:
            self.website.insert(0, data[1])
            self.username.insert(0, data[2])
            self.password.insert(0, data[3])


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.tree.bind('ButtonRelease-1>', self.edit)
        self.mainview.add_label.bind('<Button-1>', lambda event: self.addform())
        self.mainview.edit_label.bind('<Button-1>', lambda event: self.edit())
        self.mainview.delete_label.bind('<Button-1>', lambda event: self.delete())
        self.mainview.exit_label.bind('<Button-1>', lambda event: self.window.parent.destroy())

        # If there is a message display it. else forget it
        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()

        # Configure the display
        data = self.data.getall()
        self.mainview.tree.tag_configure('odd', background='#fffeee')
        self.mainview.tree.tag_configure('even', background='#eeeeef')
        self.mainview.tree.tag_configure('admin', background='pink')

        # Loop through and insert data into treeview
        for index, entry in enumerate(data):
            if entry[3]  == 'admin':
                self.mainview.tree.insert('', index, values=(entry[0], entry[2], entry[3], entry[4], entry[5]), tags=('admin', ))
            elif index % 2 == 0:
                self.mainview.tree.insert('', index, values=(entry[0], entry[2], entry[3], entry[4], entry[5]), tags=('even', ))
            else:
                self.mainview.tree.insert('', index, values=(entry[0], entry[2], entry[3], entry[4], entry[5]), tags=('odd', ))
            
    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, id=None):
        ''' Method for adding data to database '''
        website = self.dataform.website.get().strip()
        username = self.dataform.username.get().strip()
        password = self.dataform.password.get().strip()

        # Set variables for error checking
        error = 0
        messages = []

        # Do error checking
        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 there is an error, display message
        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:
            # Else everything is ok, continue
            if id:
                self.data.insert(website=website, username=username, password=password, id=id, edit=True)
            else:
                self.data.insert(website=website, username=username, password=password)

            # Destroy maiview container and redisplay
            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)
            
            msg = f'Data for {username} updataed.' if id else 'Successfully added data.'
            self._mainview(msg)

    def login(self):
        ''' Method for getting user login info '''
        username = self.loginform.username.get().strip()
        password = self.loginform.password.get().strip()

        # Check for empty fields
        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:
            # Else make query call
            result = self.data.getuser(username, password)

            # See if we got a match in the query call
            if result:
                self._mainview('You are now logged in.')
            else:
                # No match found, display message
                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')

    def edit(self):
        ''' Method for editing user information '''

        # Get the current focused item
        current = self.mainview.tree.focus()
        item = self.mainview.tree.item(current)['values']

        # If there is focus
        if item:
            self.dataform.view(self.window.container, item)
            self.dataform.add_btn['text'] = 'Edit'
            self.dataform.gen_btn['command'] = self.gen

            self.dataform.website.insert(0, item[1])
            self.dataform.username.insert(0, item[2])
            self.dataform.password.insert(0, item[3])
            id = item[0]
            
            # Go to add data form and populate
            self.dataform.add_btn['command'] = lambda: self.add(id)

    def delete(self):
        ''' Method for deleting records '''

        # Get current focused item
        current = self.mainview.tree.focus()
        item = self.mainview.tree.item(current)['values']

        # Make the database query
        action = self.data.delete(item[0])

        # True returned, record deleted
        if action:
            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('Record has been deleted.')


if __name__ == '__main__':
    root = tk.Tk()
    controller = Controller(Data(), Window(root))
    root.mainloop()
I welcome all feedback.
The only dumb question, is one that doesn't get asked.
My Github
How to post code using bbtags


Reply


Possibly Related Threads…
Thread Author Replies Views Last Post
  GUI application - code review Sr999 3 779 Jan-06-2024, 10:14 PM
Last Post: Sr999
  Code review of my rock paper scissors game Milan 0 2,036 May-25-2022, 06:59 AM
Last Post: Milan
  Review on (new) Python module: Function but Lazy Python jeertmans 3 2,437 Nov-01-2021, 06:57 PM
Last Post: ndc85430
  First time python user - Calculator code review Steamy 1 2,222 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