Skip to content
Snippets Groups Projects
Commit a0b6456e authored by Joe2.Morton@live.uwe.ac.uk's avatar Joe2.Morton@live.uwe.ac.uk
Browse files

code

parents
Branches
Tags
No related merge requests found
Main.py 0 → 100644
import tkinter as tk
from tkinter import ttk, filedialog
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
RowsNumber = 32 #The number of rows to display per page in the view section
currentPage = 0 #Current page on the view section
patientData = [] #List to store the patient data
filterOption = "All Patients" #Filter option for displaying patients
### Load File Section ###
def load_csv_data(filePath):
global patientData
try:
df = pd.read_csv(filePath) #Reads the .csv
patientData = df.values.tolist() #The dataframe is converted into a list that contains lists
except Exception as e:
print("Error loading CSV file:", e) #Prints an error when the file is not compatible
patientData = []
def upload_file():
filePath = filedialog.askopenfilename(filetypes=[("CSV Files", "*.csv")]) #Open file window opens, set to .csv files
if filePath:
fileLabel.config(text=f"File successfully loaded.") #Label to confirm file load
load_csv_data(filePath) #Loads data from the file
populate_table() #Populates the table with the loaded data
### View Patient Data Section ###
def get_filtered_data():
if filterOption == "Needs Referral":
return [p for p in patientData if p[-1] == 1] #Filters to only patients who need referring
elif filterOption == "No Referral":
return [p for p in patientData if p[-1] == 0] #Filters to only patients who don't need referring
return patientData #Return all patients by default
def populate_table():
global currentPage
for row in tree.get_children(): #Clears any existing rows in the table
tree.delete(row)
filteredData = get_filtered_data() #Check the filter to see which data to add
startIndex = currentPage * RowsNumber #Calculate starting index for the current page
endIndex = startIndex + RowsNumber #Calculate ending index for the current page
for i, patient in enumerate(filteredData[startIndex:endIndex]): #Loop through the data to display
values = list(patient) #Convert patient data to a list
#Replace nan values with empty strings
values = ["" if (isinstance(v, float) and np.isnan(v)) else v for v in values]
#Convert Encounter ID to an integer
values[0] = int(values[0])
#Round the rest of the columns to 2 decimal places
for j in range(1, len(values) - 1): #Don't include the referral columnm
try:
values[j] = round(float(values[j]), 2) #Round values to 2 decimal places
except (ValueError, TypeError):
pass #Don't round if it's not a float
#Handle referral column and assign tags for highlighting in the table
if values[-1] == 1:
values[-1] = "Needs referral"
tag = "referral" #Tags the referred patients
else:
values[-1] = "No referral"
tag = "even" if i % 2 == 0 else "odd" #Even or odd row styling
tree.insert("", "end", values=values, tags=(tag,)) #Insert the row into the table
update_buttons() #Update the buttons based on the current page
def update_buttons():
filteredData = get_filtered_data() #Gets filtered data based on the filter selected
totalPages = (len(filteredData) - 1) // RowsNumber + 1 #Gets the total number of pages
previousButton.config(state=tk.NORMAL if currentPage > 0 else tk.DISABLED) #Enables or disables the previous page button
nextButton.config(state=tk.NORMAL if currentPage < totalPages - 1 else tk.DISABLED) #Enables or disables the next page button
pageLabel.config(text=f"Page {currentPage + 1} of {totalPages}") #Updates the page label
def next_page():
global currentPage
if (currentPage + 1) * RowsNumber < len(get_filtered_data()): #Checks if there is a next page
currentPage += 1 #Adds 1 to the current page number
populate_table() #Populates the table with the new data
def prev_page():
global currentPage
if currentPage > 0: #Checks if there is a previous page
currentPage -= 1 #Minus 1 from the current page number
populate_table()
def go_to_page():
global currentPage
try:
#Gets the page number from the input box
pageNum = int(pageEntry.get()) - 1
#Checks the inputted page number exists
filteredData = get_filtered_data()
totalPages = (len(filteredData) - 1) // RowsNumber + 1
if 0 <= pageNum < totalPages:
currentPage = pageNum
populate_table()
else:
#Shows an error message if the input number is out of range
pageLabel.config(text=f"Invalid page number.")
except ValueError:
#Shows an error message if the input is not valid
pageLabel.config(text="Please enter a valid page number.")
def update_filter(option):
global filterOption, currentPage
filterOption = option #Updates the filter to the chosen option
currentPage = 0 #Resets back to the first page
populate_table() #The table is filled with the new filtered data
### Data Analysis Section ###
def analyse_data():
if not patientData:
return #There is no data to analyse
df = pd.DataFrame(patientData, columns=columns) #Converts patient data to a dataframe
df.replace("", np.nan, inplace=True) #Converts empty strings to nan
#Generates statistics for referral data
totalPatients = len(df)
needsReferral = (df["Referral"] == 1).sum()
noReferral = (df["Referral"] == 0).sum()
#Creates a pie chart for the referral statistics
fig, ax = plt.subplots(figsize=(4, 4))
ax.pie([needsReferral, noReferral], labels=["Referral", "No Referral"], autopct="%1.1f%%", colors=["#ff6666", "#66b3ff"])
ax.set_title("Referral Rate")
#Clears previous information to prevent the frame generating multiple tables
for widget in analyseFrame.winfo_children():
if widget != analyseLabel:
widget.destroy()
#Canvas for the pie chart displayed in analyseFrame
canvas = FigureCanvasTkAgg(fig, master=analyseFrame)
canvasWidget = canvas.get_tk_widget()
canvasWidget.pack(side="left", padx=(30, 20), pady=20, fill="both")
canvas.draw()
#Referral summary
summaryText = f"Total Patients: {totalPatients} Need Referral: {needsReferral} No Referral: {noReferral}"
summaryLabel = tk.Label(analyseFrame, text=summaryText, font=("Arial", 16), bg="#ffffff", justify="left")
summaryLabel.pack(side="bottom", pady=50)
#Table that lists the missing data
missingCounts = df.isna().sum().drop(["Encounter ID", "Referral"], errors='ignore')
missingTable = ttk.Treeview(analyseFrame, columns=("Column", "Missing Count"), show="headings")
missingTable.heading("Column", text="Column")
missingTable.heading("Missing Count", text="Missing Fields")
for col, count in missingCounts.items():
missingTable.insert("", "end", values=(col, count))
missingTable.pack(side="right", pady=10, padx=(20, 30), fill="both")
#Table that lists the average data
numeric_df = df.drop(columns=["Encounter ID", "Referral"], errors='ignore').apply(pd.to_numeric, errors='coerce')
averages = numeric_df.mean().round(2)
averageTable = ttk.Treeview(analyseFrame, columns=("Column", "Average Value"), show="headings")
averageTable.heading("Column", text="Column")
averageTable.heading("Average Value", text="Average Value")
for col, avg in averages.items():
averageTable.insert("", "end", values=(col, avg))
averageTable.pack(side="right", pady=10, padx=10, fill="both")
### General Functionality ###
def show_frame(frame):
#Displays the chosen frame in the interface
frame.tkraise() #The selected frame is brought to the front
if frame == analyseFrame:
analyse_data() #Automatically performs data analysis if "Analyse Patient Data" is selected
#Main application tkinter
root = tk.Tk()
try: #Starts the program in fullscreen, uses methods compatible with all operating systems
root.wm_attributes("-zoomed", True) #Linux version
except tk.TclError:
root.state('zoomed') #Windows version
root.title("Patient Data Viewer") #Window title
#root.state('zoomed') #Starts the window maximised
root.configure(bg="#ffffff") #Sets background color
#Left menu frame for navigation
menuFrame = tk.Frame(root, bg="#255fb5", width=200)
menuFrame.pack(side="left", fill="y")
#Main frame
mainFrame = tk.Frame(root, bg="#ffffff")
mainFrame.pack(side="right", expand=True, fill="both")
#Frames for different screens in the app
homeFrame = tk.Frame(mainFrame, bg="#ffffff")
uploadFrame = tk.Frame(mainFrame, bg="#ffffff")
viewFrame = tk.Frame(mainFrame, bg="#ffffff")
analyseFrame = tk.Frame(mainFrame, bg="#ffffff")
#All frames have the same properties
for frame in (homeFrame, uploadFrame, viewFrame, analyseFrame):
frame.grid(row=0, column=0, sticky="nsew")
mainFrame.grid_rowconfigure(0, weight=1)
mainFrame.grid_columnconfigure(0, weight=1)
#Menu buttons in a dictionary, the frames they open are value pairs
menuButtons = {"Home": homeFrame, "Load Patient Data": uploadFrame, "View Patient Data": viewFrame, "Analyse Patient Data": analyseFrame}
#Buttons for the menu
for text, frame in menuButtons.items():
btn = tk.Button(menuFrame, text=text, font=("Arial", 12, "bold"), bg="#255fb5", fg="white", padx=10, pady=10, relief="flat", anchor="w", command=lambda f=frame: show_frame(f))
btn.pack(fill="x", pady=5)
#Button for exiting the program
exitButton = tk.Button(menuFrame, text="Close Program", font=("Arial", 12, "bold"), bg="#255fb5", fg="white", padx=10, pady=10, relief="flat", anchor="w", command=root.quit)
exitButton.pack(side="bottom", fill="x", pady=5)
#Various labels for the frames
homeLabel = tk.Label(homeFrame, text="Welcome to the Patient Data Viewer", font=("Arial", 20, "bold"), bg="#ffffff")
homeLabel.pack(pady=20)
homeLabelHeader1 = tk.Label(homeFrame, text="Guide on using this application:", font=("Arial", 16, "bold"), bg="#ffffff")
homeLabelHeader1.pack(pady=(40,20))
homeLabelHeader2 = tk.Label(homeFrame, text="1. Loading Patient Data", font=("Arial", 16, "bold"), bg="#ffffff")
homeLabelHeader2.pack(pady=(20,20))
homeLabelBody1 = tk.Label(homeFrame, text="Before you can view your data, you need to load a valid .csv patient data file. To do this, you can go to the \"Load Patient Data\" \nsection, press the button, and locate your file within the explorer. The program will tell you if the file was loaded succesfully.", font=("Arial", 12), bg="#ffffff")
homeLabelBody1.pack(pady=(0,20))
homeLabelHeader3 = tk.Label(homeFrame, text="2. Viewing Patient Data", font=("Arial", 16, "bold"), bg="#ffffff")
homeLabelHeader3.pack(pady=(50,20))
homeLabelBody2 = tk.Label(homeFrame, text="Upon loading your .csv file successfully, you can view its contents in the \"View Patient Data\" section. Any patients needing referral\nwill be flagged in red. You can navigate to specific pages using the input and button at the centre bottom of the window. It is also\npossible to filter the table to only display only patients who need referring, and only those who do not.", font=("Arial", 12), bg="#ffffff")
homeLabelBody2.pack(pady=(0,20))
homeLabelHeader4 = tk.Label(homeFrame, text="3. Patient Data Analysis", font=("Arial", 16, "bold"), bg="#ffffff")
homeLabelHeader4.pack(pady=(50,20))
homeLabelBody3 = tk.Label(homeFrame, text="This section will display a variety of information about your .csv file. This includes the total number of patients, how many have been\nreferred and how many haven't (This is also displayed in a pie chart). There are also two tables which show how many missing fields are\npresent in each column, as well as the average value found in that column.", font=("Arial", 12), bg="#ffffff")
homeLabelBody3.pack(pady=(0,20))
uploadLabel = tk.Label(uploadFrame, text="Load Patient Data CSV", font=("Arial", 20, "bold"), bg="#ffffff")
uploadLabel.pack(pady=20)
uploadButton = tk.Button(uploadFrame, text="Press to Load File", command=upload_file, font=("Arial", 12, "bold"), bg="#286c32", fg="white", padx=25, pady=50, relief="flat")
uploadButton.pack(pady=(250, 50))
fileLabel = tk.Label(uploadFrame, text="No file selected, load a valid .csv file", font=("Arial", 12), bg="#ffffff")
fileLabel.pack(pady=5)
analyseLabel = tk.Label(analyseFrame, text="Patient Data Statistics", font=("Arial", 20, "bold"), bg="#ffffff")
analyseLabel.pack(pady=20)
#Column titles for the data tables
columns = ("Encounter ID", "End Tidal CO2", "Feed Vol", "Feed Vol Adm", "FIO2", "FIO2 Ratio", "Insp Time", "Oxygen Flow Rate", "PEEP", "PIP", "Resp Rate", "SIP", "Tidal Vol", "Tidal Vol Actual", "Tidal Vol Kg", "Tidal Vol Spon", "BMI", "Referral")
#Creates the table to view patient data
tree = ttk.Treeview(viewFrame, columns=columns, show="headings", selectmode="browse")
#Column headers and their alignment
for col in columns:
tree.heading(col, text=col, anchor="center")
tree.column(col, width=100, anchor="center")
#Colours for the patient table
tree.tag_configure("even", background="#f9f9f9")
tree.tag_configure("odd", background="#ffffff")
tree.tag_configure("referral", background="#ffcccc")
tree.pack(side="top", expand=True, fill="both")
#Horizontal scrollbar for the table
scroll_x = ttk.Scrollbar(viewFrame, orient="horizontal", command=tree.xview)
tree.configure(xscrollcommand=scroll_x.set)
scroll_x.pack(side="top", fill="x")
#Buttons for navigating through the table pages
previousButton = tk.Button(viewFrame, text="Previous", command=prev_page, font=("Arial", 12, "bold"), bg="#286c32", fg="white", padx=10, pady=5, relief="flat")
pageLabel = tk.Label(viewFrame, text="Page 1 of 1", font=("Arial", 12), bg="#ffffff")
nextButton = tk.Button(viewFrame, text="Next", command=next_page, font=("Arial", 12, "bold"), bg="#286c32", fg="white", padx=10, pady=5, relief="flat")
#Button and input box to go directly to a specific page
goButton = tk.Button(viewFrame, text="Go to page", command=go_to_page, font=("Arial", 12, "bold"), bg="#286c32", fg="white", padx=10, pady=5, relief="flat")
pageEntry = tk.Entry(viewFrame, width=5, font=("Arial", 12))
#The navigation buttons
previousButton.pack(side="left", padx=(25, 5), pady=20)
pageLabel.pack(side="left", padx=5)
nextButton.pack(side="left", padx=5, pady=5)
goButton.pack(side="left", padx=(250, 5), pady=5)
pageEntry.pack(side="left", padx=5, pady=5)
#Filter buttons
filterFrame = tk.Frame(viewFrame, bg="#ffffff")
filterFrame.pack(side="top", fill="x", pady=10)
#Button for "All Patients"
allPatientsButton = tk.Button(filterFrame, text="All Patients", command=lambda: update_filter("All Patients"), font=("Arial", 12, "bold"), bg="#286c32", fg="white", padx=10, pady=5, relief="flat")
allPatientsButton.pack(side="right", padx=(5, 25), pady=20)
# Button for "Referred" patients
referredButton = tk.Button(filterFrame, text="Referred", command=lambda: update_filter("Needs Referral"), font=("Arial", 12, "bold"), bg="#286c32", fg="white", padx=10, pady=5, relief="flat")
referredButton.pack(side="right", padx=5, pady=20)
# Button for "Not Referred" patients
notReferredButton = tk.Button(filterFrame, text="Not Referred", command=lambda: update_filter("No Referral"), font=("Arial", 12, "bold"), bg="#286c32", fg="white", padx=10, pady=5, relief="flat")
notReferredButton.pack(side="right", padx=5, pady=20)
#Makes the "Home" frame the first to appear
show_frame(homeFrame)
#Starts the Tkinter event loop
root.mainloop()
\ No newline at end of file
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment