diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1f78dd3 --- /dev/null +++ b/.gitignore @@ -0,0 +1,182 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# poetry +# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control +#poetry.lock + +# pdm +# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. +#pdm.lock +# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it +# in version control. +# https://pdm.fming.dev/#use-with-ide +.pdm.toml + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ + +# PyCharm +# JetBrains specific template is maintained in a separate JetBrains.gitignore that can +# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore +# and can be added to the global gitignore or merged into this file. For a more nuclear +# option (not recommended) you can uncomment the following to ignore the entire idea folder. +#.idea/ + + +# Swap +[._]*.s[a-v][a-z] +!*.svg # comment out if you don't need vector files +[._]*.sw[a-p] +[._]s[a-rt-v][a-z] +[._]ss[a-gi-z] +[._]sw[a-p] + +# Session +Session.vim +Sessionx.vim + +# Temporary +.netrwhist +*~ +# Auto-generated tag files +tags +# Persistent undo +[._]*.un~ + diff --git a/src/main.py b/main.py similarity index 73% rename from src/main.py rename to main.py index ab911bd..4045e4b 100644 --- a/src/main.py +++ b/main.py @@ -1,8 +1,8 @@ from tkinter import * -import Controller -import Model -import View +from src import Controller +from src import Model +from src import View if __name__ == '__main__': root = Tk() diff --git a/pictures/MVCTKVIEW.png b/pictures/MVCTKVIEW.png index c02b065..e3b409b 100644 Binary files a/pictures/MVCTKVIEW.png and b/pictures/MVCTKVIEW.png differ diff --git a/pictures/add.png b/pictures/add.png new file mode 100644 index 0000000..1fee5e1 Binary files /dev/null and b/pictures/add.png differ diff --git a/pictures/credits.txt b/pictures/credits.txt new file mode 100644 index 0000000..2968351 --- /dev/null +++ b/pictures/credits.txt @@ -0,0 +1,4 @@ +Google plus icons created by Pixel perfect - Flaticon +Cross icons created by Pixelmeetup - Flaticon +Update icons created by Freepik - Flaticon + diff --git a/pictures/delete.png b/pictures/delete.png new file mode 100644 index 0000000..44578d0 Binary files /dev/null and b/pictures/delete.png differ diff --git a/pictures/update.png b/pictures/update.png new file mode 100644 index 0000000..fc984ec Binary files /dev/null and b/pictures/update.png differ diff --git a/src/Constants.py b/src/Constants.py new file mode 100644 index 0000000..123689a --- /dev/null +++ b/src/Constants.py @@ -0,0 +1,11 @@ +import os + + +BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) +SRC_DIR = os.path.join(BASE_DIR, 'src') +PICTURES_DIR = os.path.join(BASE_DIR, 'pictures') +DATABASE_NAME = os.path.join(SRC_DIR, 'books.db') +ADD_PICTURE = os.path.join(PICTURES_DIR, 'add.png') +UPDATE_PICTURE = os.path.join(PICTURES_DIR, 'update.png') +DELETE_PICTURE = os.path.join(PICTURES_DIR, 'delete.png') + diff --git a/src/Controller.py b/src/Controller.py index defd0b3..bbb4f76 100644 --- a/src/Controller.py +++ b/src/Controller.py @@ -1,5 +1,3 @@ -from tkinter import * - class Controller: def __init__(self, model, view): self.model = model @@ -7,7 +5,7 @@ def __init__(self, model, view): self.view.buttonAdd["command"] = self.addDataToLbx self.view.buttonUpdate["command"] = self.updateDataFromLBX self.view.buttonRemove["command"] = self.removeDataFromLBX - self.view.listbox.bind('<>', self.loadLbxDataToEntry) + self.view.treeview.bind('<>', self.loadDataToEntry) self.loadDataToLBX() def loadDataToLBX(self): @@ -26,12 +24,13 @@ def updateDataFromLBX(self): self.updateDB() self.loadDataToLBX() - def loadLbxDataToEntry(self, event=None): - title = self.view.getCursorTitle() - author = self.view.getCursorAuthor() + def loadDataToEntry(self, event=None): + title = self.view.getCursorTitle() or '' + author = self.view.getCursorAuthor() or '' + id_ = self.view.getCursorID() or '' self.view.setTitle(title) self.view.setAuthor(author) - print("ID: ", self.view.getCursorID(), "\nTITLE: ", title, "\nAUTHOR: ", author) + print("ID: ", id_, "\nTITLE: ", title, "\nAUTHOR: ", author) def addToDB(self): title = self.view.getTitleData() @@ -46,4 +45,6 @@ def updateDB(self): def removeDataFromDB(self): id = self.view.getCursorID() - self.model.deleteDataFromTable(id) + if id: + self.model.deleteDataFromTable(id) + diff --git a/src/Model.py b/src/Model.py index 001f78f..4079024 100644 --- a/src/Model.py +++ b/src/Model.py @@ -1,8 +1,9 @@ import sqlite3 +from src import Constants class Model: def __init__(self): - self.con = sqlite3.connect('books.db') + self.con = sqlite3.connect(Constants.DATABASE_NAME) self.cur = self.con.cursor() def addToTable(self, title, author): @@ -11,7 +12,6 @@ def addToTable(self, title, author): print(self.cur.fetchall()) self.con.commit() - def updateTable(self, id, title, author): self.values = (str(title), str(author), int(id)) self.cur.execute("UPDATE Books SET title = ?, author= ? WHERE id = ?", self.values) @@ -30,4 +30,5 @@ def getAllData(self): result = self.cur.fetchall() print(result) self.con.commit() - return result \ No newline at end of file + return result + diff --git a/src/View.py b/src/View.py index 0e3a0ef..f92adf4 100644 --- a/src/View.py +++ b/src/View.py @@ -1,36 +1,84 @@ -from tkinter import * +import tkinter as tk +import tkinter.ttk as ttk +from src import Constants + + class View: def __init__(self, root): self.root = root self.root.title("Library Database") + self.root.geometry('900x500+0+0') self.createWidgets() def createWidgets(self): - self.labelTitle = Label(self.root, text="Title: ") + self.root.columnconfigure(0, weight=1) + self.root.columnconfigure(1, weight=1) + self.root.columnconfigure(2, weight=1) + self.root.rowconfigure(3, weight=1) + + self.labelTitle = ttk.Label(self.root, text="Title: ") self.labelTitle.grid(row=0, column=0, padx=5, pady=5) - self.entryTitleTextVar = StringVar() - self.entryTitle = Entry(self.root, textvariable=self.entryTitleTextVar) - self.entryTitle.grid(row=0, column=1, padx=5, pady=5) + self.entryTitleTextVar = tk.StringVar() + self.entryTitle = ttk.Entry(self.root) + self.entryTitle.configure(textvariable=self.entryTitleTextVar) + self.entryTitle.configure(font='Geogia 16 normal') + self.entryTitle.grid(row=0, column=1, columnspan=2, padx=5, pady=5, sticky='ew') - self.labelAuthor = Label(self.root, text="Author: ") + self.labelAuthor = ttk.Label(self.root, text="Author: ") self.labelAuthor.grid(row=1, column=0, padx=5, pady=5) - self.entryAuthorTextVar = StringVar() - self.entryAuthor = Entry(self.root, textvariable=self.entryAuthorTextVar) - self.entryAuthor.grid(row=1, column=1, padx=5, pady=5) - - self.buttonAdd = Button(self.root, text="Add") - self.buttonAdd.grid(row=2, column=0, pady=5) - - self.buttonUpdate = Button(self.root, text="Update") - self.buttonUpdate.grid(row=2, column=1, pady=5) - - self.buttonRemove = Button(self.root, text="Remove") - self.buttonRemove.grid(row=2, column=2, pady=5) - - self.listbox = Listbox(self.root, width=60) - self.listbox.grid(row=3, column=1, padx=5, pady=5) + self.entryAuthorTextVar = tk.StringVar() + self.entryAuthor = ttk.Entry(self.root) + self.entryAuthor.configure(textvariable=self.entryAuthorTextVar) + self.entryAuthor.configure(font='Geogia 16 normal') + self.entryAuthor.grid(row=1, column=1, columnspan=2, padx=5, pady=5, sticky='ew') + + self.addPicure = tk.PhotoImage(file=Constants.ADD_PICTURE) + self.buttonAdd = ttk.Button(self.root) + self.buttonAdd.configure(text="Add") + self.buttonAdd.configure(image=self.addPicure) + self.buttonAdd.configure(compound='left') + self.buttonAdd.grid(row=2, column=0, pady=5, sticky='ew') + + self.updatePicture = tk.PhotoImage(file=Constants.UPDATE_PICTURE) + self.buttonUpdate = ttk.Button(self.root) + self.buttonUpdate.configure(text="Update") + self.buttonUpdate.configure(image=self.updatePicture) + self.buttonUpdate.configure(compound='left') + self.buttonUpdate.grid(row=2, column=1, padx=5, pady=5, sticky='ew') + + self.deletePicture = tk.PhotoImage(file=Constants.DELETE_PICTURE) + self.buttonRemove = ttk.Button(self.root) + self.buttonRemove.configure(text="Remove") + self.buttonRemove.configure(image=self.deletePicture) + self.buttonRemove.configure(compound='left') + self.buttonRemove.grid(row=2, column=2, pady=5, sticky='ew') + + # treeview stuffs + self.treeview = ttk.Treeview(self.root) + self.treeview.configure(show='headings') + self.treeview.configure(columns=('id', 'title', 'author')) + self.treeview.heading('id', text='Id') + self.treeview.heading('title', text='Title') + self.treeview.heading('author', text='Author') + self.treeview.column('id', width=70, minwidth=70, stretch=False) + self.treeview.column('title', minwidth=70) + self.treeview.column('author', minwidth=70) + self.treeview.grid(row=3, column=0, columnspan=3, sticky='nsew') + + self.scrollbar = ttk.Scrollbar(self.root) + self.scrollbar.grid(row=3, column=3, sticky='ns') + + self.scrollbar.configure(command=self.treeview.yview) + self.treeview.configure(yscrollcommand=self.scrollbar.set) + + # style + style = ttk.Style() + style.theme_use('clam') + style.configure('.', font='Georgia 16 normal') + style.configure('Treeview', rowheight=40) + style.configure('Treeview.Heading', font='Georgia 16 normal') def getTitleData(self): return self.entryTitleTextVar.get() @@ -44,22 +92,38 @@ def setTitle(self, title): def setAuthor(self, author): self.entryAuthorTextVar.set(author) + def getTreeviewSelection(self): + selections = self.treeview.selection() + if selections: + selection = selections[0] + values = self.treeview.item(selection)['values'] + return values + + return None + def getCursorID(self, event=None): - selectedLbData = self.listbox.curselection() - id = self.listbox.get(selectedLbData)[0] - return id + selectedData = self.getTreeviewSelection() + if selectedData: + return selectedData[0] + + return None def getCursorTitle(self): - selectedLbData = self.listbox.curselection() - title = self.listbox.get(selectedLbData)[1] - return title + selectedData = self.getTreeviewSelection() + if selectedData: + return selectedData[1] + + return None def getCursorAuthor(self): - selectedLbData = self.listbox.curselection() - author = self.listbox.get(selectedLbData)[2] - return author + selectedData = self.getTreeviewSelection() + if selectedData: + return selectedData[2] + + return None def setListbox(self, data): - self.listbox.delete(0, END) + self.treeview.delete(*self.treeview.get_children()) for values in data: - self.listbox.insert(END, values) + self.treeview.insert('', 'end', values=(values)) + diff --git a/src/books.db b/src/books.db index 36b402a..1a4b784 100644 Binary files a/src/books.db and b/src/books.db differ