Skip to content

A simple library book management system built with python and tkinter

Notifications You must be signed in to change notification settings

bheki-maenetja/lboro-library

Repository files navigation

Loughborough Library

A simple library book management system built with python and the Tkinter GUI library.

Getting Started

Installation

  • Clone this repository by running the terminal command git clone [email protected]:bheki-maenetja/lboro-library.git
  • In the root folder run the terminal command pipenv shell
  • In the root folder run the terminal command pipenv install to install all necessary packages and modules

Deployment

  • To run the program locally enter python menu.py in the terminal

Technology Used

  • Python 3
  • Tkinter library
  • Pipenv

Overview

Loughborough Library is a simple library book management system built primarily in python with the use of the Tkinter GUI library. The system allows users to search for books either by title or by category/genre. Additionally, books can be loaned out and returned to the library. The user can keep track of books that are out on loan or overdue. Users can also view analytics relating to book usage in the library - for instance the user can view the most popular genres or the number of books loaned out per year.

The Home Page

Users can search for books and loan them out

Users can see which books are on loan and overdue

Users can view visualisations of book usage data

Development

The project was developed over the course of 4 weeks. This project was originally a coursework assignment for the Introduction to Programming module in Loughborough University's undergraduate Computer Science course. It was the final project for the module, counting for 80% of the module's marks.

Design Pattern

  • The application uses a standard Model-View-Controller design pattern with the model and view code residing in database.py and menu.py respectively. The controller code sits in several files in the folder titled operations.
# Code for the major GUI components and home page
# The use of classes would have made this far simpler but it was prohibited as part of the assignment

# ====================== MAIN WINDOW & GLOBAL VARIABLES ======================  
## Window Setup ==============================================================
root = tk.Tk()
root.title('Library Management System')
root.geometry('900x630')
root.resizable(False, False)

## Global Variables ==========================================================
page_manager = dict()

# ============================= UTILITY FUNCTIONS ============================ 
def alert(message, is_error=True):
    """
    WHAT DOES THIS FUNCTION DO?
        * this function displays pop up message that can indicate an error or confirmation
    """
    if is_error:
        messagebox.showwarning(message=message)
    else:
        messagebox.showinfo(message=message)

def format_text(input_str, standard_length):
    """
    WHAT DOES THIS FUNCTION DO?
        * this formats a given string to ensure that it has a standard length
    """
    str_len = len(input_str)
    if str_len == standard_length:
        return input_str
    elif str_len > standard_length:
        return input_str[:standard_length - 3] + "..."
    elif str_len < standard_length:
        return input_str + " "*(standard_length - str_len)

def validate_numeric_entry(val):
    """
    WHAT DOES THIS FUNCTION DO?
        * this functions ensure that the value entered into an entry is numeric
    """
    return re.match('^[0-9]*$', val) is not None and len(val) < 5

# ============================== PAGE CONTAINER ==============================
## The Main Page Container ===================================================
def build_page_container():
    """
    WHAT DOES THIS FUNCTION DO?
        * builds the notebook that contains the books, loan manager and system info pages
    """
    page_notebook = ttk.Notebook(master=root)
    page_notebook.add(tk.Frame(), text="Home")

    books_page = build_books_page(page_notebook)
    loan_manager_page = build_loan_manager_page(page_notebook)
    analytics_page = build_analytics_page(page_notebook)
    system_info_page = build_system_info_page(page_notebook)

    page_notebook.add(books_page, text="Books")
    page_notebook.add(loan_manager_page, text="Loan Manager")
    page_notebook.add(analytics_page, text="Analytics")
    page_notebook.add(system_info_page, text="System Info")

    page_notebook.bind('<<NotebookTabChanged>>', lambda e: page_change())

    return page_notebook

# ================================= HOME PAGE ================================
## Home Page UI Components ===================================================
def build_home_page():
    """
    WHAT DOES THIS FUNCTION DO?
        * builds the home page
    """
    home_frame = tk.Frame(master=root, height=100, width=100)
    home_frame.columnconfigure(0, weight=1, minsize=root.winfo_height())
    home_frame.rowconfigure(0, weight=1, minsize=root.winfo_width())
    home_frame.rowconfigure(1, weight=1, minsize=root.winfo_width())

    hero_section = build_hero_section(home_frame)
    button_section = build_button_section(home_frame)
    hero_section.grid(row=0, column=0, sticky="nesw")
    button_section.grid(row=1, column=0, sticky="nesw")

    return home_frame

def build_hero_section(master_frame):
    """
    WHAT DOES THIS FUNCTION DO?
        * builds the top section of the home page
    """
    hero_section = tk.Frame(master=master_frame, bg="#4B0082")
    hero_section.rowconfigure(0, weight=1, minsize=root.winfo_width())
    hero_section.rowconfigure(1, weight=1, minsize=root.winfo_width())
    hero_section.columnconfigure(0, weight=1, minsize=root.winfo_height())

    heading_font = tkFont.Font(family="Verdana", size=40, weight="bold")
    sub_heading_font = tkFont.Font(family="Verdana", size=20, slant="italic")

    heading = tk.Label(
        master=hero_section, 
        text="Loughborough Library", 
        font=heading_font, 
        bg="#4B0082", 
        fg="white"
    )

    sub_heading = tk.Label(
        master=hero_section, 
        text=f"{dt.strftime(dt.now(), '%d %B, %Y')}", 
        font=sub_heading_font, 
        fg="white",
        bg="#4B0082", 
    )

    heading.grid(row=0, column=0, sticky="ws", padx=20)
    sub_heading.grid(row=1, column=0, sticky="wn", padx=20)

    return hero_section

def build_button_section(master_frame):
    """
    WHAT DOES THIS FUNCTION DO?
        * builds the bottom section of the home page
    """
    button_section = tk.Frame(master=master_frame, bg="#4B0082")
    button_info = [
        ('Books', '#1E90FF', lambda e: transition(pages_index=1)), 
        ('Loan Manager', '#228B22', lambda e: transition(pages_index=2)), 
        ('Analytics', '#FF8C00', lambda e: transition(pages_index=3)), 
        ('System Info', 'grey', lambda e: transition(pages_index=4))
    ]
    button_font = tkFont.Font(family="helvetica", size=30, weight="bold", slant="italic")

    for i in range(2):
        button_section.columnconfigure(i, weight=1, minsize=25)
        button_section.rowconfigure(i, weight=1, minsize=25)
        for j in range(2):
            new_button_info = button_info[j + i * 2]
            new_button = tk.Label(
                button_section, 
                text=new_button_info[0], 
                font=button_font, 
                bg=new_button_info[1], 
                highlightthickness=5, 
                fg="white", 
                relief=tk.RAISED
            )
            new_button.bind('<Button-1>', new_button_info[2])
            new_button.grid(row=i, column=j, sticky="nesw")
    
    return button_section

## Home Page Functionality ===================================================
page_manager['home_page'] = build_home_page()

Book and Log Data

  • All the data that the program handles are stored in text files. Data about each individual book is stored in database.txt. Log data (where a log is a record of a book being checked out or returned) is stored in logfile.txt.
  • Both database.txt and logfile.txt sit in the folder titled system_data. Copies of both files sit in the root of the project; these files are used as backups and can also be used to provide seed data for the application.
# Model code for handling book data 

# ============================= GLOBAL VARIABLES =============================
dir_path = os.path.dirname(os.path.realpath(__file__))
database_file = os.path.join(dir_path, "database.txt")
log_file = os.path.join(dir_path, "logfile.txt")

# =================================== BOOKS ==================================
## Getting & Updating Books from file ========================================
def get_all_books():
    """
    PARAMATERS
        * None
    RETURN VALUES
        * a list of dictionaries with each dictionary holding information about an indivdual book
    WHAT DOES THIS FUNCTION DO?
        * This function retrieves all book information in the database.txt file and returns it as a list
    """
    try:
        database = open(database_file, "r")
        return [json.loads(book) for book in database]
    except:
        print('Something went wrong...')
    database.close()

def get_book_by_id(book_id):
    """
    PARAMATERS
        * book_id -> an integer representing the unique identifier of a book
    RETURN VALUES
        * a single dictionary that holds information about a particular book
    WHAT DOES THIS FUNCTION DO?
        * This function locates the information of an indivdual book in the database and returns it
    """
    try:
        book_str = linecache.getline(database_file, book_id)
        return json.loads(book_str)
    except:
        print('something went wrong...')

def update_book(book_id, book_obj):
    """
    PARAMATERS
        * book_id -> an integer representing the unique identifier of a book
        * book_obj -> a dictionary with the newly updated information of a book
    RETURN VALUES
        * None
    WHAT DOES THIS FUNCTION DO?
        * This function locates a book in the database and updates its information
    """
    try:
        for book in fileinput.input(database_file, inplace=True):
            book_dict = json.loads(book)
            if book_dict['id'] == book_id:
                print(json.dumps(book_obj))
            else:
                print(json.dumps(book_dict))
    except:
        print('Error -- Could not update book record')

Project Constraints

  • It should be noted that while this project was being developed as a coursework assignment the use of custom classes was prohibited; this effected the program code - particulary the code for the GUI - making it necessarily more verbose.

Reflection

Challenges

  • This project was my first time using the Tkinter GUI library, therefore it should come as no surprise that almost all challenging aspects of the creation of this program relate to this simple fact. Getting to grips with Tkinter was the overarching challenge for this project and the prohibition of custom classes did not make that challenge any easier.

Room for Improvement

  • Aside from some aesthetic changes I feel that the only part of the application in need of major improvement is the search functionality on the 'Books' page. The textual search functionality can be quite slow at times and many of the category checkboxes are mutually exclusive; meaning that if a user ticks merely 2 or 3 boxes they oftentimes end up with no search results.

Future Features

  • Database: whilst the text files serve as an adequate storage space for data, they lack the capabilities needed to make the application truly dynamic. A future version of this project will make use of a proper SQL database which should allow for improved search functionality and data analytics.
  • User Accounts: the next update will feature individual user accounts. Users will be able to create and manage their own book libraries and share access to their libraries with other users.

About

A simple library book management system built with python and tkinter

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages