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