From dc888ad3228b1e3ad3a7320d12142e7d4c38de2e Mon Sep 17 00:00:00 2001
From: Wojciech Wojnowski <wojciech.wojnowski@pg.edu.pl>
Date: Sat, 5 Oct 2024 13:04:39 +0000
Subject: [PATCH] Upload New File

---
 main.py | 416 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 416 insertions(+)
 create mode 100644 main.py

diff --git a/main.py b/main.py
new file mode 100644
index 0000000..4ed6b1b
--- /dev/null
+++ b/main.py
@@ -0,0 +1,416 @@
+import tkinter as tk
+from tkinter import ttk
+import matplotlib.pyplot as plt
+from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
+from tkinter import filedialog
+from matplotlib import colormaps
+
+# Start the main app:
+root = tk.Tk()
+root.title('RAPI beta 0.7')
+root.geometry('1000x600')
+root.iconbitmap('Icon.ico')
+
+# Define the color palette. This can be based on https://materialui.co/colors
+colors = {
+    'foreground': '#ffffff',
+    'background': '#EEEEEE',
+    'accent': '#FFCC80',
+    'text': 'black',
+    'inactive': '#AAAAAA'  # Grayed-out color for inactive elements
+}
+
+# Configure the background:
+root.configure(bg=colors['background'], padx=0, pady=0)
+
+# Create and configure a style to be used in tabs, labels, and other elements
+s = ttk.Style()
+s.theme_create("RAPI_theme", parent="alt", settings={
+    'TLabel': {
+        'configure': {
+            'background': colors['foreground'],
+            'foreground': colors['text'],
+            'padding': [0, 5, 7, 0]}},
+    'TFrame': {
+        'configure': {
+            'background': colors['foreground'],
+            'borderwidth': 0,
+            'highlightcolor': 'red'}},
+    'TRadiobutton': {
+        'configure': {
+            'background': colors['foreground'],
+            'foreground': colors['text'],
+            'padding': [5, 0, 0, 0],
+        }},
+    'TMenubutton': {
+        'configure': {
+            'background': colors['accent'],
+            'foreground': colors['text'],
+            'padding': [5, 0, 0, 0],
+            'width': '30'
+        }},
+    'TMenu': {
+        'configure': {
+            'background': colors['accent'],
+            'foreground': colors['text'],
+            'width': '20',
+            'arrowcolor': colors['text'],
+        }},
+})
+
+s.configure('RAPI_theme', relief='flat',
+            background=colors['background'],
+            foreground=colors['text'],
+            insertcolor=colors['text'],
+            selectbackground=colors['accent']
+            )
+
+s.theme_use("RAPI_theme")
+
+
+def save_image():
+    ftypes = [('PNG file', '.png'), ('SVG file', '.svg'), ('All files', '*')]
+    filename = filedialog.asksaveasfilename(filetypes=ftypes, defaultextension='.png')
+    plt.savefig(filename, bbox_inches='tight', dpi=300)
+
+
+def reset_scores():
+    for criterion in criteria:
+        criterion.valuevar.set(0.0)
+        criterion.color.set('white')
+    for dropdown in dropdowns:
+        dropdown.reset_option()
+
+    chromatography_var.set('no')
+    toggle_chromatography()  # Resets state according to radio button
+    create_plot()
+
+
+def call_info_popup():
+    win = tk.Toplevel()
+    win_frame = tk.Frame(win, width=500, background=colors['foreground'])
+    win_frame.pack()
+    win.iconbitmap('Icon.ico')
+    win.wm_title('About and citation info')
+    text0 = ttk.Label(win_frame, text='RAPI is a metric tool for ...',
+                      wraplength=380, justify='left')
+    text0.grid(column=0, row=0, padx=8, pady=8, sticky='w')
+    text1 = ttk.Label(win_frame, text='If you use RAPI, please cite:', wraplength=280, justify='left')
+    text1.grid(column=0, row=1, padx=8, pady=8, sticky='w')
+    text2 = tk.Text(win_frame, width=50, height=6, wrap='word', bg=colors['background'])
+    citation = 'Paweł Mateusz Nowak, Wojciech Wojnowski, Natalia Manousi, Victoria Samanidou, Justyna Płotka-Wasylka,' \
+               'Red Analytical Performance Index (RAPI) and software: the missing tool for assessing methods' \
+               'in terms of analytical effectiveness, doi.org/XX.XXXX/XXXXXXXXXX'
+    text2.grid(column=0, row=2, padx=8, pady=8, sticky='w')
+    text2.insert(tk.END, citation)
+    text3 = ttk.Label(win_frame, text='Most recent version of the source code can be found at:', wraplength=320,
+                      justify='left')
+    text3.grid(column=0, row=3, padx=8, pady=8, sticky='w')
+    text4 = tk.Text(win_frame, width=50, height=1, wrap='word', bg=colors['background'])
+    source_link = 'git.pg.edu.pl/p174235/rapi'
+    text4.grid(column=0, row=4, padx=8, pady=8, sticky='w')
+    text4.insert(tk.END, source_link)
+    text5 = ttk.Label(win_frame, text='© 2024 P. Nowak, W. Wojnowski, N. Manousi, V. Samanidou, J. Płotka-Waskylka, '\
+                                      'available under the MIT License', wraplength=380,
+                      justify='left')
+    text5.grid(column=0, row=5, padx=8, pady=8, sticky='w')
+
+
+def create_menu():
+    menu = tk.Menu()
+    root.config(menu=menu)
+    file_menu = tk.Menu(menu)
+    file_menu.config(background=colors['background'],
+                     activeborderwidth=5,
+                     activeforeground=colors['text'],
+                     foreground=colors['text'],
+                     tearoff=0)
+
+    file_menu.add_command(label='Save the image', command=save_image)
+    file_menu.add_command(label='Re-set', command=reset_scores)
+    about_menu = tk.Menu(menu)
+    about_menu.config(background=colors['background'],
+                      activeborderwidth=5,
+                      activeforeground=colors['text'],
+                      foreground=colors['text'],
+                      tearoff=0)
+    menu.add_cascade(label='File', menu=file_menu)
+    menu.add_cascade(label='About', menu=about_menu)
+    about_menu.add_command(label='Info', command=call_info_popup)
+
+
+create_menu()
+
+coordinates = {
+    0: [(-8.719, 12), (-14.107, -4.584), (0, -14.833), (14.107, -4.584), (8.719, 12)],
+    1: [(8.719, 12), (21.796, 30), (-21.796, 30), (-8.719, 12)],
+    2: [(20.497, 34), (8.199, 50.927), (-8.199, 50.927), (-20.497, 34)],
+    3: [(-14.107, -4.584), (-35.267, -11.459), (0, -37.082), (0, -14.833)],
+    4: [(0, -14.833), (0, -37.082), (35.267, -11.459), (14.107, -4.584)],
+    5: [(14.107, -4.584), (35.267, -11.459), (21.796, 30), (8.719, 12)],
+    6: [(-8.719, 12), (-21.796, 30), (-35.267, -11.459), (-14.107, -4.584)],
+    7: [(-26.002, 30), (-45.901, 23.535), (-50.968, 7.94), (-38.67, -8.987)],
+    8: [(38.67, -8.987), (50.968, 7.94), (45.901, 23.535), (26.002, 30)],
+    9: [(-36.567, -15.459), (-36.567, -36.382), (-23.301, -46.02), (-3.402, -39.554)],
+    10: [(36.567, -15.459), (36.567, -36.382), (23.301, -46.02), (3.402, -39.554)],
+}
+
+color_values = {
+    'white': 0,
+    '#fff5f0': 2.5,
+    '#fca183': 5,
+    '#e53228': 7.5,
+    '#67000d': 10}
+
+# Create the two main frames of the GUI
+left_frame = tk.Frame(root, bd=0, padx=2, pady=0, width=500, height=500, bg=colors['foreground'],
+                      highlightbackground=colors['background'], highlightthickness=8)
+left_frame.pack(side='left', anchor='n', fill='both', expand=True)
+
+right_frame = tk.Frame(root, bd=0, padx=2, pady=0, width=500, height=500, bg=colors['background'],
+                       highlightbackground=colors['background'], highlightthickness=8,
+                       highlightcolor=colors['background'])
+right_frame.pack(side='right', anchor='n')
+
+
+class Criterion:
+    def __init__(self, number):
+        self.number = number
+        self.valuevar = tk.DoubleVar()
+        self.valuevar.set(0.0)
+        self.color = tk.StringVar()
+        self.color.set('white')
+        self.optionvar = tk.StringVar()
+        self.optionvar.set('select')
+        self.coordinates = coordinates[number]
+
+
+criteria = []
+for i in range(0, 11):
+    criteria.append(Criterion(i))
+
+
+def clear_frame(frame):
+    frame.destroy()
+    global right_frame
+    right_frame = tk.Frame(root, bd=0, padx=2, pady=0, width=500, height=500, bg=colors['foreground'],
+                           highlightbackground=colors['background'], highlightthickness=8,
+                           highlightcolor=colors['background'])
+    right_frame.pack(side='right', anchor='n')
+
+
+def calculate_score():
+    active_criteria = criteria[1:9] if chromatography_var.get() == 'no' else criteria[1:11]
+    score = sum(color_values[c.color.get()] for c in active_criteria)
+    return round(score / len(active_criteria) * 10, 1)
+
+
+def create_plot(event=None):
+    plt.close()
+    clear_frame(right_frame)
+
+    fig, ax = plt.subplots(figsize=(10, 10), dpi=150, facecolor=colors['foreground'])
+
+    for i, criterion in enumerate(criteria[1:], start=1):
+        face_color = criterion.color.get()
+        edge_color = 'black'
+        if chromatography_var.get() == 'no' and i in (9, 10):
+            edge_color = 'white'
+            face_color = 'white'  # Hide polygons for criteria 9 and 10
+
+        polygon = plt.Polygon(criterion.coordinates, facecolor=face_color, edgecolor=edge_color,
+                            joinstyle='round')
+        ax.add_patch(polygon)
+
+    ax.autoscale()
+    ax.set_aspect('equal')
+    center_score = calculate_score()
+
+    central_polygon_color = 'white' if center_score == 0 else colormaps['Reds'](int(255 * (center_score / 100)))
+    central_polygon = plt.Polygon(criteria[0].coordinates, facecolor=central_polygon_color, edgecolor='black')
+    ax.add_patch(central_polygon)
+
+    text_color = 'white' if center_score >= 80 else 'black'
+    ax.text(0, 0, str(center_score), ha='center', va='center', fontsize=14, color=text_color)
+
+    ax.axis('off')
+
+    canvas = FigureCanvasTkAgg(figure=fig, master=right_frame)
+    plot_widget = canvas.get_tk_widget()
+    plot_widget.pack(side='top', padx=8, pady=8)
+
+
+class Dropdown:
+    def __init__(self, criterion, options, label_text, row):
+        self.var = criterion.color
+        self.options = options
+        self.row = row
+        self.dropdown_var = tk.StringVar(value='not confirmed')
+
+        # Create the dropdown menu
+        self.dropdown_menu = ttk.OptionMenu(left_frame, self.dropdown_var, self.dropdown_var.get(),
+                                            *self.options.keys())
+        self.dropdown_menu.config(width=50)  # Adjust the width here
+        self.dropdown_menu.grid(row=self.row + 1, column=0, padx=8, sticky='W')
+
+        # Initialize label
+        self.label = ttk.Label(left_frame, text=label_text)
+        self.label.grid(row=self.row, column=0, padx=8, sticky='W')
+
+        # Overlay label for inactive state
+        # Using a lighter gray to ensure text remains visible
+        self.overlay = tk.Label(left_frame, bg='#DDDDDD', width=20, height=1)
+        self.overlay.place_forget()  # Initially hidden
+
+        # Trace changes
+        self.dropdown_var.trace('w', self.change_dropdown)
+
+    def change_dropdown(self, *args):
+        self.var.set(self.options[self.dropdown_var.get()])
+        create_plot()
+
+    def reset_option(self):
+        self.dropdown_var.set('not confirmed')
+        self.var.set('white')
+
+    def set_inactive(self, inactive=True):
+        if inactive:
+            self.overlay.place(in_=self.dropdown_menu, relwidth=1, relheight=1)
+        else:
+            self.overlay.place_forget()
+
+
+def toggle_chromatography(*args):
+    if chromatography_var.get() == 'yes':
+        dropdown9.label.config(foreground=colors['text'])  # Active state color
+        dropdown10.label.config(foreground=colors['text'])
+        dropdown9.dropdown_menu.config(state='normal')
+        dropdown10.dropdown_menu.config(state='normal')
+        dropdown9.set_inactive(False)
+        dropdown10.set_inactive(False)
+    else:
+        dropdown9.reset_option()
+        dropdown10.reset_option()
+        dropdown9.dropdown_menu.config(state='disabled')
+        dropdown10.dropdown_menu.config(state='disabled')
+        dropdown9.label.config(foreground=colors['inactive'])  # Inactive state color
+        dropdown10.label.config(foreground=colors['inactive'])
+        dropdown9.set_inactive(True)
+        dropdown10.set_inactive(True)
+    create_plot()
+
+
+
+# Dropdowns
+dropdown1 = Dropdown(criterion=criteria[1], options={
+    'not confirmed': 'white',
+    'error > 20%': '#fff5f0',
+    '10% < error  ≤ 20%': '#fca183',
+    '5% < error ≤ 10%': '#e53228',
+    'error ≤ 5%': '#67000d'},
+                     row=1,
+                     label_text='1. Trueness (relative error, %)')
+
+dropdown2 = Dropdown(criterion=criteria[2], options={
+    'not confirmed': 'white',
+    'recovery < 80%, recovery  > 120%': '#fff5f0',
+    'recovery ∈ (80% : 90%⟩, ⟨110% : 120%)': '#fca183',
+    'recovery ∈ (90% : 95%⟩, ⟨105% : 110%)': '#e53228',
+    'recovery > 95%, recovery < 105%': '#67000d'},
+                     row=3,
+                     label_text='2. Recovery (%)')
+
+dropdown3 = Dropdown(criterion=criteria[3], options={
+    'not confirmed': 'white',
+    'RSD > 20%': '#fff5f0',
+    '10% < RSD  ≤ 20%': '#fca183',
+    '5% < RSD ≤ 10%': '#e53228',
+    'RSD ≤ 5%': '#67000d'},
+                     row=5, label_text='3. Intra-day precision (RSD, %)')
+
+dropdown4 = Dropdown(criterion=criteria[4], options={
+    'not confirmed': 'white',
+    'RSD > 25%': '#fff5f0',
+    '15% < RSD  ≤ 25%': '#fca183',
+    '7% < RSD ≤ 15%': '#e53228',
+    'RSD ≤ 7%': '#67000d'},
+                     row=7, label_text='4. Inter-day precision (RSD, %)')
+
+dropdown5 = Dropdown(criterion=criteria[5], options={
+    'not confirmed': 'white',
+    'LOQ > 50% e.c.': '#fff5f0',
+    '10% e.c. < LOQ  ≤ 50% e.c.': '#fca183',
+    '1% e.c. < LOQ ≤ 10% e.c.': '#e53228',
+    'LOQ ≤ 1% e.c.': '#67000d'},
+                     row=9, label_text='5. LOQ (%)')
+
+dropdown6 = Dropdown(criterion=criteria[6], options={
+    'not confirmed': 'white',
+    '< (LOQ : 10×LOQ)': '#fff5f0',
+    '≥ (LOQ : 10×LOQ), ≤ (LOQ : 30×LOQ)': '#fca183',
+    '≥ (LOQ : 30×LOQ), ≤ (LOQ : 100×LOQ)': '#e53228',
+    '> (LOQ : 100×LOQ)': '#67000d'},
+                     row=11, label_text='6. Calibration (linearity) range')
+
+dropdown7 = Dropdown(criterion=criteria[7], options={
+    'not confirmed': 'white',
+    'linearity < 0.900': '#fff5f0',
+    '0.990 > linearity  ≥ 0.900': '#fca183',
+    '0.999 > linearity  ≥ 0.990': '#e53228',
+    'linearity ≥ 0.999': '#67000d'},
+                     row=13, label_text='7. Linearity (R2)')
+
+dropdown8 = Dropdown(criterion=criteria[8], options={
+    'not confirmed': 'white',
+    'confirmed against 1 factor': '#fff5f0',
+    'confirmed against 2 factors': '#fca183',
+    'confirmed against 3 factors': '#e53228',
+    'confirmed against > 3 factors': '#67000d'},
+                     row=15, label_text='8. Robustness')
+
+chrom_label = ttk.Label(left_frame, text='Does the method involve chromatographic separation?')
+chrom_label.grid(row=17, column=0, padx=8, sticky='W')
+
+chromatography_var = tk.StringVar(value='no')
+chrom_radio1 = ttk.Radiobutton(left_frame, text='Yes', variable=chromatography_var, value='yes', style='TRadiobutton')
+chrom_radio2 = ttk.Radiobutton(left_frame, text='No', variable=chromatography_var, value='no', style='TRadiobutton')
+chrom_radio1.grid(row=18, column=0, sticky='W', padx=8)
+chrom_radio2.grid(row=18, column=0, sticky='W', padx=58)
+
+# Configure the radio buttons to avoid dark frame highlight on selection
+chrom_radio1.configure(takefocus=False)
+chrom_radio2.configure(takefocus=False)
+
+chromatography_var.trace('w', toggle_chromatography)
+
+dropdown9 = Dropdown(criterion=criteria[9], options={
+    'not confirmed': 'white',
+    '≤ 1 000': '#fff5f0',
+    '∈ (1 000 : 10 000⟩': '#fca183',
+    '∈ (10 000 : 100 000⟩': '#e53228',
+    '> 100 000': '#67000d'},
+                     row=19, label_text='9. Mean no. of theoretical plates')
+
+dropdown10 = Dropdown(criterion=criteria[10], options={
+    'not confirmed': 'white',
+    'resolution < 0.5': '#fff5f0',
+    '1.0 > resolution ≥ 0.5': '#fca183',
+    '1.5 > resolution  ≥ 1.0': '#e53228',
+    'resolution ≥ 1.5': '#67000d'},
+                      row=21, label_text='10. Resolution of the worst-separated peaks')
+
+# Set initial states for dropdown labels and states
+toggle_chromatography()
+
+dropdowns = [dropdown1, dropdown2, dropdown3, dropdown4, dropdown5, dropdown6, dropdown7, dropdown8, dropdown9,
+             dropdown10]
+
+create_plot()
+
+
+def main():
+    while True:
+        root.mainloop()
+
+
+main()
-- 
GitLab