From 005e592992c9ac3cb337f9759b8488a7bf8b062d Mon Sep 17 00:00:00 2001 From: Wojciech Wojnowski <wojciech.wojnowski@pg.edu.pl> Date: Thu, 11 Nov 2021 09:32:43 +0000 Subject: [PATCH] Update AGREE.py --- AGREE.py | 2570 +++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 1753 insertions(+), 817 deletions(-) diff --git a/AGREE.py b/AGREE.py index 05aba7d..3903393 100644 --- a/AGREE.py +++ b/AGREE.py @@ -1,357 +1,137 @@ -''' -W.Wojnowski 2021 -''' -# imports: -import tkinter as tk -from tkinter import ttk -from tkinter import messagebox +# AGREE, Analytical GREennEss Calculator +# F. Pena-Pereira, W.Wojnowski, M. Tobiszewski 2020 +# v.0.5 (beta) + +# imports +from tkinter import * import matplotlib.pyplot as plt from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg from matplotlib.colors import LinearSegmentedColormap +from tkinter import ttk from tkinter import filedialog +import tkinter.messagebox +import webbrowser from math import log from fpdf import FPDF import os from datetime import datetime -# start the main app: -root = tk.Tk() -root.title('AGREEprep - Analytical Greenness Metric for Sample Preparation') -root.geometry('760x450') -root.iconbitmap('AGREEPrep_icon.ico') -# fix the window dimensions: -root.resizable(0, 0) - -# define the color palette. This can be based on https://flatuicolors.com -colors = { - 'foreground': '#ffffff', - 'background': '#dfe4ea', - 'accent': '#70a1ff', - 'text': 'black' -} +# ***************** SPLASH SCREEN ******************************* -# configure the background: -root.configure(bg=colors['background'], padx=8, pady=8) - -# create and configure a style to be used in tabs and labels -s = ttk.Style() -s.theme_create("AGREE_2.0_theme", parent="alt", settings={ - "TNotebook": { - "configure": { - 'background': colors['background'], - 'borderwidth': 0, - 'tabmargins': [0, 0, 0, 0], - 'padding': [0, 0, 5, 0], - 'relief': 'flat'}}, - "TNotebook.Tab": { - "configure": { - 'padding': [11, 1], - 'background': colors['accent'], - 'foreground': colors['text'], - # change the colour of a dashed frame around the title of the - # selected frame to the foreground colour, so that it is not visible: - 'focuscolor': colors['foreground'], - 'borderwidth': 0, - 'bordercolor': 'red'}, - "map": { - "background": [("selected", colors['foreground'])], - "expand": [("selected", [1, 1, 1, 0])]}}, - 'TLabel': { - 'configure': { - 'background': colors['foreground'], - 'foreground': colors['text'], - 'padding': [5, 5, 5, 5]}}, - 'TFrame': { - 'configure': { - 'background': colors['foreground'], - 'borderwidth': 0}}, - 'TEntry': { - 'configure': { - 'background': 'red', - 'fieldbackground': colors['background'], - 'foreground': colors['text'], - 'insertcolor': colors['text'], - 'selectbackground': colors['accent'], - }}, - 'TButton': { - 'configure': { - 'background': colors['accent'], - 'focuscolor': colors['accent'], - 'foreground': colors['text'], - 'anchor': 'center'}, - 'map': { - 'relief': [('pressed', 'groove')] - }}, - 'TRadiobutton': { - 'configure': { - 'focuscolor': colors['foreground'], - 'background': colors['foreground'], - 'foreground': colors['text'], - 'indicatorcolor': colors['background']}, - "map": { - "background": [("active", colors['accent']), - ("pressed", colors['accent']), - ("disabled", colors['accent'])], - "foreground": [("active", colors['text'])], - 'indicatorcolor': [('selected', colors['accent'])] - }}, - 'TCheckbutton': { - 'configure': { - 'focuscolor': colors['foreground'], - 'background': colors['foreground'], - 'foreground': colors['text'], - 'indicatorcolor': colors['background']}, - "map": { - "background": [("active", colors['accent']), - ("pressed", colors['accent']), - ("disabled", colors['accent'])], - "foreground": [("active", colors['text'])], - 'indicatorcolor': [('selected', colors['accent'])] - }}, - 'TMenubutton': { - 'configure': { - 'background': colors['accent'], - 'foreground': colors['text'], - 'width': '20' - }}, - 'TMenu': { - 'configure': { - 'background': colors['accent'], - 'foreground': colors['text'], - 'width': '20', - 'arrowcolor': colors['text'], - }}, -}) - -s.theme_use("AGREE_2.0_theme") - -# to remove relief from the TEntry widget -# based on https://stackoverflow.com/questions/44383730/ -s.layout('AGREE_2.0_theme', [ - ('Entry.highlight', { - 'sticky': 'nswe', - 'children': - [('Entry.border', { - 'border': '1', - 'sticky': 'nswe', - 'children': - [('Entry.padding', { - 'sticky': 'nswe', - 'children': - [('Entry.textarea', - {'sticky': 'nswe'})] - })] - })] - })]) - -s.configure('AGREE_2.0_theme', relief='flat', - background=colors['background'], - foreground=colors['text'], - insertcolor=colors['text'], - selectbackground=colors['accent'] - ) - - -def call_info_popup(): - win = tk.Toplevel() - win_frame = tk.Frame(win, width=500, background=colors['foreground']) - win_frame.pack() - win.wm_title('About and citation info') - title = ttk.Label(win_frame, text='Some title', font=('TkDefaultFont', 10, 'bold'), justify='left') - title.grid(column=0, row=0, padx=8, pady=8, sticky='w') - text = ttk.Label(win_frame, text='About info, license, citation info (to be filled in after publication) \n' - 'The most recent version of the code is available at ' - 'https://git.pg.edu.pl/p174235/agreeprep', wraplength=280, justify='left') - text.grid(column=0, row=1, padx=8, pady=8, sticky='w') - - cite_button = ttk.Button(win_frame, text="Close", command=win.destroy) - cite_button.grid(row=4, column=0, padx=8, pady=8) +root = Tk() + +# show no frame +root.overrideredirect(True) +# splash screen dimensions: W x H + posx + posy +root.geometry('800x500+300+200') +image_file = "AGREEsplash.png" +# use Tkinter's PhotoImage for .gif and .png files +image = PhotoImage(file=image_file) + +canvas = Canvas(root, height=500, width=800, bg='black', bd=0, highlightthickness=0) +canvas.create_image(400, 250, anchor=CENTER, image=image) +canvas.pack() +# show the splash screen for 5000 milliseconds then destroy +root.after(5000, root.destroy) +root.mainloop() + +# **************** MAIN WINDOW ******************************* + +root = Tk() + +# app title: +root.title('AGREE: Analytical GREennEss Calculator ') + +# default app size: +root.geometry('800x500') +root.minsize(800, 500) +# root.configure(bg='white') +# create the small icon in the task bar: +root.iconbitmap('icon_eTk_icon.ico') -def save_image(): - ftypes = [('PNG file', '.png'), ('SVG file', '.svg'), ('All files', '*')] +def clearFrame(frame): + frame.destroy() + global rightFrame + rightFrame = Frame(root, width=300, height=450, padx=20) + rightFrame.pack(side=RIGHT) + + +# Image save dialog: +def saveImage(): + ftypes = [('PNG file', '.png'), ('All files', '*')] filename = filedialog.asksaveasfilename(filetypes=ftypes, defaultextension='.png') # save the plot in the specified path; the 'tight' option removes the whitespace from around the figure: - plt.savefig(filename, bbox_inches='tight', dpi=300) + plt.savefig(filename, bbox_inches='tight') -def reset_scores(): - for criterion in criteria: - criterion.textvar.set('1.0') - criterion.optionvar.set('Select') - criterion.valuevar.set('Input') - criterion.optionvar_a.set('Select') - criterion.optionvar_b.set('Select') +# temporary placeholder function: +def doNothing(): + print("ok ok I won't...") - criteria[0].radiovar.set(1) - criteria[1].radiovar.set(5) - criteria[2].radiovar.set(2) - criteria[3].radiovar.set(4) - criteria[4].radiovar.set(1) - criteria[5].radiovar.set(3) - criteria[6].radiovar.set(1) - criteria[7].radiovar.set(4) - criteria[8].radiovar.set(2) - criteria[9].radiovar.set(3) - - create_plot() - - -def temp_func(): - pass - - -def generate_report(): - report = Report(tabs, criteria) - report.savePDF() - - -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'], - activebackground=colors['accent'], - foreground=colors['text'], - tearoff=0) - - file_menu.add_command(label='Save image', command=save_image) - file_menu.add_command(label='Re-set', command=reset_scores) - file_menu.add_command(label='Report', command=generate_report) - about_menu = tk.Menu(menu) - about_menu.config(background=colors['background'], - activeborderwidth=5, - activeforeground=colors['text'], - activebackground=colors['accent'], - 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() - -# create the two main frames of the GUI -left_frame = tk.Frame(root, bd=0, padx=2, pady=0, width=300, height=500, bg=colors['background']) -left_frame.pack(side='left', anchor='n') - -right_frame = tk.Frame(root, bd=1, padx=2, pady=2, width=500, height=500, bg=colors['foreground']) -right_frame.pack(side='right', anchor='n') - - -# notebook which will house the 10 tabs (in the left frame) -class Notebook: - - def __init__(self, parent): - self.parent = parent - self.notebook = ttk.Notebook(self.parent, height=500, width=300) - self.notebook.pack() - - def add_tab(self, tab, text): - self.notebook.add(tab, text=text) - - -tab_bar = Notebook(left_frame) - - -class Tab: - - def __init__(self, tab_no, title, text1, text2, criterion): - self.title = title - self.parent = tab_bar - self.frame = tk.Frame(self.parent.notebook, bg=colors['foreground']) - self.parent.add_tab(self.frame, text=tab_no) - self.header = ttk.Label(self.frame, text=title, wraplength=280, font=('TkDefaultFont', 10, 'bold')) - self.header.grid(column=0, row=0, sticky='w') - self.label1 = ttk.Label(self.frame, text=text1, wraplength=280) - self.label1.grid(column=0, row=1, sticky='w') - self.label2 = ttk.Label(self.frame, text=text2, wraplength=280) - self.label2.grid(column=0, row=2, sticky='w') - - self.criterion = criterion - - self.ratio_label = ttk.Label(self.frame, text='Modify the default weight:') - self.ratio_label.place(rely=0.91, relx=0.025, anchor='sw') - - self.radio_1 = ttk.Radiobutton(self.frame, text='1', variable=self.criterion.radiovar, command=create_plot, - value=1) - self.radio_1.place(rely=0.95, relx=0.05, anchor='sw') - self.radio_2 = ttk.Radiobutton(self.frame, text='2', variable=self.criterion.radiovar, command=create_plot, - value=2) - self.radio_2.place(rely=0.95, relx=0.15, anchor='sw') - self.radio_3 = ttk.Radiobutton(self.frame, text='3', variable=self.criterion.radiovar, command=create_plot, - value=3) - self.radio_3.place(rely=0.95, relx=0.25, anchor='sw') - self.radio_4 = ttk.Radiobutton(self.frame, text='4', variable=self.criterion.radiovar, command=create_plot, - value=4) - self.radio_4.place(rely=0.95, relx=0.35, anchor='sw') - self.radio_5 = ttk.Radiobutton(self.frame, text='5', variable=self.criterion.radiovar, command=create_plot, - value=5) - self.radio_5.place(rely=0.95, relx=0.45, anchor='sw') - - -class Criterion: - - def __init__(self, number): - self.number = number - self.textvar = tk.StringVar() - self.textvar.set('1.0') - self.radiovar = tk.IntVar() - self.optionvar = tk.StringVar() - self.optionvar.set('Select') - self.valuevar = tk.StringVar() - self.valuevar.set('Input') - # add optionvars for tab 7: - self.optionvar_a = tk.StringVar() - self.optionvar_a.set('Select') - self.optionvar_b = tk.StringVar() - self.optionvar_b.set('Select') - - -# create the 10 criteria values. They can be later called as -# list elements, e.g. to get the value of criterion 3: criteria[2].textvar.get() -criteria = [] -for i in range(1, 11): - criteria.append(Criterion(i)) - -# assign the default weights (this could be done using itemgetter or itertools.compress, but -# that would require importing additional packages): -criteria[0].radiovar.set(1) -criteria[1].radiovar.set(5) -criteria[2].radiovar.set(2) -criteria[3].radiovar.set(4) -criteria[4].radiovar.set(1) -criteria[5].radiovar.set(3) -criteria[6].radiovar.set(1) -criteria[7].radiovar.set(4) -criteria[8].radiovar.set(2) -criteria[9].radiovar.set(3) - - -def calculate_score(): - indv_scores = 0 - weights = 0 - for criterion in criteria: - indv_scores += (float(criterion.textvar.get()) * criterion.radiovar.get()) - weights += criterion.radiovar.get() - score = indv_scores / weights - return score - - -# a function for restarting the right frame (to clear the memory after each change) -# it is then used when there is a need to change the plot -def clear_frame(frame): - frame.destroy() - global right_frame - right_frame = tk.Frame(root, bd=1, padx=2, pady=2, width=500, height=500, bg=colors['foreground']) - right_frame.pack(side='right', anchor='n') +# create the popup window with some additional information: +def popup_bonus(): + win = Toplevel() + win.wm_title("About the Analytical Greenness Calculator") + win.iconbitmap('icon_eTk_icon.ico') + + def callback(event): + webbrowser.open_new(event.widget.cget("text")) + + popup_label1 = Label(win, text='v. 0.5 2020', justify=LEFT) + popup_label1.grid(row=0, column=0, padx=8, pady=8) + + popup_label2 = Label(win, text=r'https://git.pg.edu.pl/p174235/AGREE', fg='blue', cursor='hand2', justify=LEFT) + popup_label2.grid(row=1, column=0, padx=8, pady=8) + popup_label2.bind('<Button-1>', callback) + + popup_label3 = Label(win, + text='The Analytical Greenness calculator is a tool intended to aid the User in evaluating the environmental and occupational hazards associated with a particular analytical procedure based on the 12 principles of Green Analytical Chemistry. The result of the assessment is translated into a graph that is easy to interpret and contains an overall score, as well as an indication to what degree the evaluated procedure conforms to each of the 12 principles. It can be used to compare different methods in order to select the one with the lowest environmental impact, or to identify possible trouble spots in terms of "greenness" during conceptualization and development of novel analytical procedures.', + wraplength=300, justify=LEFT, bg='white') + popup_label3.grid(row=2, column=0, padx=8, pady=8) + + popup_button = Button(win, text="Close", command=win.destroy) + popup_button.grid(row=3, column=0, padx=8, pady=8) + +# create the popup window with citing information: +def cite_as(): + win = Toplevel() + win.wm_title('Cite As') + win.iconbitmap('icon_eTk_icon.ico') -# connect a float in range 0.0 : 1.0 to a colour in a spectrum from red to yellow to green -# (256^3 discrete colour values): + def callback(event): + webbrowser.open_new(event.widget.cget("text")) + + cite_label1 = Label(win, text="If you are using the AGREE calculator in your research, please cite: \n" + "Pena-Pereira F., Wojnowski W., Tobiszewski M., AGREE – Analytical GREEnness metric approach and software,\n" + " Analytical Chemistry (2020), DOI: 10.1021/acs.analchem.0c01887", justify=LEFT) + cite_label1.grid(row=0, column=0, padx=8, pady=8) + + cite_label2 = Label(win, text=r'https://doi.org/10.1021/acs.analchem.0c01887', fg='blue', cursor='hand2', justify=LEFT) + cite_label2.grid(row=1, column=0, padx=8, pady=8) + cite_label2.bind('<Button-1>', callback) + + cite_label3 = Label(win, text='BibTeX entry:', justify=LEFT) + cite_label3.grid(row=2, column=0, padx=8, pady=8) + + cite_label4 = Label(win, + text='@article{AC:penapereira2020a,\n' + 'author = {Pena-Pereira Francisco and Wojnowski Wojciech and Tobiszewski Marek},\n' + 'title = {AGREE – Analytical GREEnness metric approach and software},\n' + 'journal = {Analytical Chemistry},\n' + 'year = {2020},\n' + 'url = http://doi.org/10.1021/acs.analchem.0c01887\n' + '}', + wraplength=500, justify=LEFT, bg='white') + + cite_label4.grid(row=3, column=0, padx=8, pady=8) + + cite_button = Button(win, text="Close", command=win.destroy) + cite_button.grid(row=4, column=0, padx=8, pady=8) + + +# connect a float in range 0.0 : 1.0 to a colour in a spectrum from red to yellow to green (256 discrete colour values): def colorMapper(value): cmap = LinearSegmentedColormap.from_list('rg', ["red", "yellow", "green"], N=256) mapped_color = int(value * 255) @@ -359,540 +139,1696 @@ def colorMapper(value): return color -def change_dropdown(criterion, tab_choices, *args): - if criterion.optionvar.get() == 'Select': - criterion.textvar.set(1.0) - else: - criterion.textvar.set(tab_choices[criterion.optionvar.get()]) - create_plot() - - -def create_plot(event=None): - # the event=None is passed so that the entry.bind - # does not return a positional argument; - # close the current plot window (otherwise the generated plots remain open in the memory): - plt.close() - - # re-generate the frame: - clear_frame(right_frame) - - fig = plt.figure(figsize=(3, 3), dpi=150, facecolor=colors['foreground']) - ax = plt.subplot(projection='polar') - plt.axis('off') # hide the axes - - scores = [] - weights = [] - - for criterion in criteria: - scores.append(float(criterion.textvar.get())) - weights.append(criterion.radiovar.get()) - - # just having pi as a variable to avoid importing numpy or math - pi = 3.141592653589793 - # modify the weights so that the differences between lengths of slices are less extreme: - weights = [i + 4.5 for i in weights] - - ''' - # generate the thetas once and then just use the values to avoid importing numpy: - import numpy as np - theta = np.linspace(0.0, 2 * pi, N, endpoint=False) - print(theta) - ''' - - theta = [0.0, 0.62831853, 1.25663706, 1.88495559, 2.51327412, 3.14159265, 3.76991118, 4.39822972, 5.02654825, - 5.65486678] - radii = weights - width = 2 * pi / len(scores) - - bars = ax.bar(theta, radii, width=width, bottom=0.0, edgecolor='black', linewidth=0.5) - ax.set_theta_direction(-1) # theta increasing clockwise - ax.set_theta_zero_location("N") # theta=0 at the top - - # annotate the slices with numbers: - for i in range(10): - ax.text(theta[i], 4.75, criteria[i].number, verticalalignment='center', horizontalalignment='center', - color='black', fontsize=6) - - # link the slice (bar) colour with the score: - for i, bar in zip(range(10), bars): - bar.set_facecolor(colorMapper(scores[i])) - - # add a Circle artist in the middle to depict the final value: - circle = plt.Circle((0, 0), radius=4, facecolor=colorMapper(calculate_score()), edgecolor='black', - linewidth=0.5, transform=ax.transData._b, ) +# function for refreshing the canvas: +def destroyCanvas(canvas): + canvas.destroy() + + +# the final score variable: +final_score = StringVar() + + +def printScore(): + try: + global score + score = (var_1 * weight_1.get() + + var_2 * weight_2.get() + + var_3 * weight_3.get() + + var_4 * weight_4.get() + + var_5 * weight_5.get() + + var_6 * weight_6.get() + + var_7 * weight_7.get() + + var_8 * weight_8.get() + + var_9 * weight_9.get() + + var_10 * weight_10.get() + + var_11 * weight_11.get() + + var_12 * weight_12.get()) / (weight_1.get() + weight_2.get() + weight_3.get() + weight_4.get() + weight_5.get() + + weight_6.get() + weight_7.get() + + weight_8.get() + weight_9.get() + weight_10.get() + weight_11.get() + weight_12.get()) + + # set the final score as a text string rounded to 2 decimals: + final_score.set(str(round(score, 2))) + + print(' \n Total score: %s, rounded: %s' % (str(score), str(round(score, 2)))) + print('Criteria scores:') + except NameError: + tkinter.messagebox.showerror(title='Name Error', message='Please set all 12 variables.') + + +# a function to refresh the chart: +def chartPlotter(event=None): + printScore(), clearFrame(rightFrame), pieChart(), print_variables() + + +# interface for assigning custom weights to the 12 variables: +def weightChoice(row, column, tab, weightVar): + # the interface is disabled until the checkbox is checked + chckbxVar = StringVar() + # disable by default + chckbxVar.set('disabled') + + # the default weight is set to 2 + radioVar = IntVar() + radioVar.set(2) + radio_1 = ttk.Radiobutton(tab, text='1', variable=radioVar, value=1) + radio_2 = ttk.Radiobutton(tab, text='2', variable=radioVar, value=2) + radio_3 = ttk.Radiobutton(tab, text='3', variable=radioVar, value=3) + radio_4 = ttk.Radiobutton(tab, text='4', variable=radioVar, value=4) + + radio_1.grid(row=row + 1, column=column, sticky='sw', padx=(70, 0)) + radio_2.grid(row=row + 1, column=column, sticky='sw', padx=(100, 0)) + radio_3.grid(row=row + 1, column=column, sticky='sw', padx=(130, 0)) + radio_4.grid(row=row + 1, column=column, sticky='sw', padx=(160, 0)) + + radio_1.config(state=DISABLED) + radio_2.config(state=DISABLED) + radio_3.config(state=DISABLED) + radio_4.config(state=DISABLED) + + def printRadioVar(): + weightVar.set(radioVar.get()) + chartPlotter() + + weight_button = ttk.Button(tab, text='Set weight', command=printRadioVar) + weight_button.grid(row=row + 1, column=column, sticky='sw', padx=(190, 0)) + weight_button.config(state=DISABLED) + + def printCheckbox(): + radios = (radio_1, radio_2, radio_3, radio_4) + if chckbxVar.get() == 'disabled': + radioVar.set(2) + weightVar.set(2) + for radio in radios: + radio.config(state=DISABLED if chckbxVar.get() == 'disabled' else NORMAL) + weight_button.config(state=DISABLED if chckbxVar.get() == 'disabled' else NORMAL) + + ttk.Checkbutton(tab, text='Modify the default weights', command=lambda: [printCheckbox()], variable=chckbxVar, onvalue='enabled', offvalue='disabled').grid(row=row, column=column, + columnspan=4, sticky='w', padx=8, + pady=(60, 0)) + Label(tab, text='Weight: ').grid(row=row + 1, column=column, sticky='sw', padx=8) + + +# ********** Main menu *********************************************************************************** +menu = Menu(root) + +# configure the menu: +root.config(menu=menu) + +FileMenu = Menu(menu) +AboutMenu = Menu(menu) + +# add drop-down functionality: +menu.add_cascade(label='File', menu=FileMenu) +menu.add_cascade(label='About', menu=AboutMenu) +AboutMenu.add_command(label='Info', command=popup_bonus) +# FileMenu.add_separator() +FileMenu.add_command(label='Save image', command=saveImage) + +AboutMenu.add_command(label='How to Cite', command=cite_as) + +# ******** Statusbar ************* +def createStatusBar(): + status = ttk.Label(root, textvariable=status_string, bd=1, relief=SUNKEN, anchor=W) + status.pack(side=BOTTOM, fill=X) + + +status_string = StringVar() +# status_string.trace('w', createStatusBar) +status_string.set('test test') +status = ttk.Label(root, textvariable=status_string, borderwidth=1, relief=SUNKEN, anchor=W) +status.pack(side=BOTTOM, fill=X) + +# ******** Two separate frames ****** +leftFrame = Frame(root, bd=1, width=300, height=450) +rightFrame = Frame(root, width=300, height=450, padx=20) +bottomFrame = Frame(root, bd=1) + +leftFrame.pack(side=LEFT, anchor=N) +rightFrame.pack(side=RIGHT) +bottomFrame.pack(side=BOTTOM, anchor=W) + +# ************************* Tabs *************************** +# create tabs for each rule: +tab_parent = ttk.Notebook(leftFrame, height=400) +tab1 = ttk.Frame(tab_parent) +tab2 = ttk.Frame(tab_parent) +tab3 = ttk.Frame(tab_parent) +tab4 = ttk.Frame(tab_parent) +tab5 = ttk.Frame(tab_parent) +tab6 = ttk.Frame(tab_parent) +tab7 = ttk.Frame(tab_parent) +tab8 = ttk.Frame(tab_parent) +tab9 = ttk.Frame(tab_parent) +tab10 = ttk.Frame(tab_parent) +tab11 = ttk.Frame(tab_parent) +tab12 = ttk.Frame(tab_parent) + +# add tabs to the tab parent: +tab_parent.add(tab1, text="1") +tab_parent.add(tab2, text="2") +tab_parent.add(tab3, text="3") +tab_parent.add(tab4, text="4") +tab_parent.add(tab5, text="5") +tab_parent.add(tab6, text="6") +tab_parent.add(tab7, text="7") +tab_parent.add(tab8, text="8") +tab_parent.add(tab9, text="9") +tab_parent.add(tab10, text="10") +tab_parent.add(tab11, text="11") +tab_parent.add(tab12, text="12") + +# ****** Initial variables ******** + +weight_1 = IntVar() +weight_2 = IntVar() +weight_3 = IntVar() +weight_4 = IntVar() +weight_5 = IntVar() +weight_6 = IntVar() +weight_7 = IntVar() +weight_8 = IntVar() +weight_9 = IntVar() +weight_10 = IntVar() +weight_11 = IntVar() +weight_12 = IntVar() + + +weight_1.set(2) +weight_2.set(2) +weight_3.set(2) +weight_4.set(2) +weight_5.set(2) +weight_6.set(2) +weight_7.set(2) +weight_8.set(2) +weight_9.set(2) +weight_10.set(2) +weight_11.set(2) +weight_12.set(2) + + +# Begin with default values of variables to be able to generate the chart right away: +var_1 = 1.0 +var_2 = 1.0 +var_3 = 1.0 +var_4 = 1.0 +var_5 = 1.0 +var_6 = 1.0 +var_7 = 1.0 +var_8 = 1.0 +var_9 = 1.0 +var_10 = 1.0 +var_11 = 1.0 +var_12 = 1.0 + +# function for re-setting the graph: +# function for re-setting the graph: +def resetAllValues(): + global var_1, var_2, var_3, var_4, var_5, var_6, var_7, var_8, var_9, var_10, var_11, var_12 + global weight_1, weight_2, weight_3, weight_4, weight_5, weight_6, weight_7, weight_8, weight_9, weight_10, weight_11, weight_12 + global var_1_text, amount_var, var_3_text, var_4_text, var_5a_text, var_5b_text, amount_var7, amount_var8a, amount_var8b, amount_var9, var_9_text, var_10_text, var_11a_text, amount_var11b + + # re-set the variables: + var_1 = 1.0 + var_2 = 1.0 + var_3 = 1.0 + var_4 = 1.0 + var_5 = 1.0 + var_6 = 1.0 + var_7 = 1.0 + var_8 = 1.0 + var_9 = 1.0 + var_10 = 1.0 + var_11 = 1.0 + var_12 = 1.0 + + # re-set the weights: + weight_1.set(2) + weight_2.set(2) + weight_3.set(2) + weight_4.set(2) + weight_5.set(2) + weight_6.set(2) + weight_7.set(2) + weight_8.set(2) + weight_9.set(2) + weight_10.set(2) + weight_11.set(2) + weight_12.set(2) + + # re-set the dialogs + var_1_text.set('select') + amount_var.set('input') + var_3_text.set('select') + var_4_text.set('select') + var_5a_text.set('select') + var_5b_text.set('select') + # clear the list of selected CAS in Tab6: + clear_list() + amount_var7.set('input') + amount_var8a.set('input') + amount_var8b.set('input') + amount_var9.set('input') + var_9_text.set('select') + var_10_text.set('select') + var_11a_text.set('select') + amount_var11b.set(0.0) + # clear the selection boxes in Tab12: + global varA, varB, varC, varD, varE, varF, varG + varA.set(0.0) + varB.set(0.0) + varC.set(0.0) + varD.set(0.0) + varE.set(0.0) + varF.set(0.0) + varG.set(0.0) + # re-load the inactive weight selection interfaces: + tabs = (tab1, tab2, tab3, tab4, tab5, tab6, tab7, tab8, tab9, tab10, tab11, tab12) + weights = (weight_1, weight_2, weight_3, weight_4, weight_5, weight_6, weight_7, weight_8, weight_9, weight_10, weight_11, weight_12) + weight_index = 0 + for tab in tabs: + weightChoice(10, 0, tab, weights[weight_index]) + weight_index += 1 + + # re-draw the graph: + chartPlotter() + + +# add the reset functionality to the file menu: +FileMenu.add_command(label='Re-set the scores', command=resetAllValues) + + +# function for updating the status bar: +def updateStatusBar(): + global status_string + status_string.set(' scores: | (1) %s | (2) %s | (3) %s | (4) %s | (5) %s | (6) %s | (7) %s | (8) %s | (9) %s | (10) %s | (11) %s | (12) %s |' + % (str(round(var_1, 2)), + str(round(var_2, 2)), + str(round(var_3, 2)), + str(round(var_4, 2)), + str(round(var_5, 2)), + str(round(var_6, 2)), + str(round(var_7, 2)), + str(round(var_8, 2)), + str(round(var_9, 2)), + str(round(var_10, 2)), + str(round(var_11, 2)), + str(round(var_12, 2)) + )) + + +# generate the pie chart (plot) with a circle decal in the middle: +def pieChart(): # weights, labels, colors + + colors = [colorMapper(var_1), colorMapper(var_2), colorMapper(var_3), colorMapper(var_4), colorMapper(var_5), colorMapper(var_6), colorMapper(var_7), colorMapper(var_8), colorMapper(var_9), + colorMapper(var_10), colorMapper(var_11), colorMapper(var_12)] + + weights = [weight_1.get(), weight_2.get(), weight_3.get(), weight_4.get(), weight_5.get(), weight_6.get(), weight_7.get(), weight_8.get(), weight_9.get(), weight_10.get(), weight_11.get(), + weight_12.get()] + labels = ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12'] + + index_value = float(final_score.get()) + + fig, ax = plt.subplots(figsize=(3, 3), dpi=150) + ax.clear() + ax.axis('equal') + radius = 1.0 + pie2 = ax.pie(weights, radius=radius, colors=colors, labeldistance=(radius * 0.875), labels=labels, + rotatelabels=False, startangle=90, counterclock=False, + wedgeprops={"edgecolor": "black", 'linewidth': 1}, textprops={'fontsize': (radius * 10)}) + + plt.setp(pie2[1], rotation_mode="anchor", ha="center", va="center") + for tx in pie2[1]: + rot = tx.get_rotation() + tx.set_rotation(rot) + # if you want rotated labels: + # tx.set_rotation(rot+ 90 + (1 - rot // 180) * 180) + + circle = plt.Circle(xy=(0, 0), radius=(radius * 0.75), facecolor=colorMapper(index_value), edgecolor='black', + linewidth=1) plt.gca().add_artist(circle) - # add the final score in the middle: - ax.text(0.0, 0.0, str(round(calculate_score(), 2)), + ax.text(0.5, 0.5, str(index_value), verticalalignment='center', horizontalalignment='center', - color='black', fontsize=20) + transform=ax.transAxes, + color='black', fontsize=(radius * 40)) - canvas = FigureCanvasTkAgg(figure=fig, master=right_frame) + fig.tight_layout() # for exporting a compact figure + + # Pack the figure into a canvas: + canvas = FigureCanvasTkAgg(fig, master=rightFrame) # A tk.DrawingArea. plot_widget = canvas.get_tk_widget() - plot_widget.pack(side='top') - - # print('final score: %s' % calculate_score()) - - -# start the GUI with a default plot -create_plot() - - -class Report: - def __init__(self, tabs, criteria): - self.pdf = FPDF('P', 'mm', 'A4') # 'P' - Portrait - self.pdf.set_font('Helvetica', '', 10) - self.pdf.add_page() - self.pdf.set_margins(left=25, top=20) - self.tabs = tabs - self.criteria = criteria - plt.savefig('temp_figure.png', bbox_inches='tight') - # insert image (image, x, y, width): - self.pdf.image('temp_figure.png', 107, 10, 80) - # delete the temp file from drive: - os.remove('temp_figure.png') - - self.fill_color = (240, 240, 240) - - # insert title (Helvetica, 'B'old, 14 pt): - self.pdf.set_font('Helvetica', 'B', 18) - self.pdf.ln(20) - self.pdf.cell(100, 12, 'AGREEprep') - self.pdf.set_font('Helvetica', '', 12) - self.pdf.ln(10) - self.pdf.multi_cell(55, 4, 'Analytical Greenness Metric for Sample Preparation') - self.pdf.ln(10) - self.pdf.set_font('Helvetica', '', 10) - self.pdf.cell(100, 12, datetime.now().strftime("%d/%m/%Y %H:%M:%S")) - self.pdf.ln(30) - - def fieldColor(score): - x = colorMapper(score)[0] * 255 - y = colorMapper(score)[1] * 255 - z = colorMapper(score)[2] * 255 - self.pdf.set_fill_color(x, y, z) - - def create_header(): - self.pdf.set_font('Helvetica', 'B', 10) - self.pdf.cell(12, 6, '#', align='C') - self.pdf.cell(120, 6, 'Criterion', align='L') - self.pdf.cell(12, 6, 'Score', align='C') - self.pdf.cell(12, 6, 'Weight', align='C') - self.pdf.ln(8) - self.pdf.set_font('Helvetica', '', 10) - - create_header() - - def create_report_field(number, tabs, criterion, text): - self.pdf.cell(12, 12, number, border=1, align='C') - self.pdf.set_fill_color(240, 240, 240) - self.pdf.set_font('Helvetica', 'B', 10) - self.pdf.cell(120, 6, tabs.title, border=1, ln=2, align='L', fill=True) - self.pdf.set_font('Helvetica', '', 8) - self.pdf.cell(120, 6, text, border=1, ln=0, align='L', fill=False) - self.pdf.set_font('Helvetica', '', 10) - - # modify the position of the following cells to ensure proper alignment: - x = self.pdf.get_x() - y = self.pdf.get_y() - self.pdf.set_xy(x, y - 6) - - # set the colour of the score field: - fieldColor(float(criterion.textvar.get())) - self.pdf.cell(12, 12, str(round(float(criterion.textvar.get()), 2)), border=1, fill=True, align='C') - self.pdf.set_fill_color(240, 240, 240) - self.pdf.cell(12, 12, str(criterion.radiovar.get()), border=1, fill=True, align='C') - self.pdf.ln(15) - - create_report_field('1.', self.tabs[0], self.criteria[0], - ('Sample preparation placement: ' + self.criteria[0].optionvar.get())) - - create_report_field('2.', self.tabs[1], self.criteria[1], - ('Mass [g] or volume [mL] of problematic materials: ' + self.criteria[1].valuevar.get())) - - create_report_field('3.', self.tabs[2], self.criteria[2], - (self.criteria[2].optionvar.get())) - - create_report_field('4.', self.tabs[3], self.criteria[3], - ('Mass [g] or volume [mL] of waste: ' + self.criteria[3].valuevar.get())) - - create_report_field('5.', self.tabs[4], self.criteria[4], - ('Mass [g] or volume [mL] of the sample: ' + self.criteria[4].valuevar.get())) - - create_report_field('6.', self.tabs[5], self.criteria[5], - ('Hourly sample throughput: ' + self.criteria[5].valuevar.get())) - - create_report_field('7.', self.tabs[6], self.criteria[6], - ('No. of sample prep. steps: ' + self.criteria[6].optionvar_a.get() + - '; degree if automation: ' + self.criteria[6].optionvar_b.get())) - - create_report_field('8.', self.tabs[7], self.criteria[7], - ('Approximate energy consumption per analysis [W]: ' + - self.criteria[7].valuevar.get())) - - create_report_field('9.', self.tabs[8], self.criteria[8], - (self.criteria[8].optionvar.get())) - - create_report_field('10.', self.tabs[9], self.criteria[9], - ('No. of distinct hazards: ' + self.criteria[9].optionvar.get())) - - def savePDF(self): - ftypes = [('PDF file', '.pdf'), ('All files', '*')] - filename = filedialog.asksaveasfilename(filetypes=ftypes, defaultextension='.pdf') - # save the pdf - self.pdf.output(filename, 'F') - # open the file in the system default viewer: - os.system('start ' + filename) + plot_widget.pack(side=TOP) + # print(weight_12.get()) + + +# ************************************** + +# generate the pie chart (plot) with a circle decal in the middle, together with annotations explaining the numbered fields: +def annotatedPieChart(): # weights, labels, colors + + colors = [colorMapper(var_1), colorMapper(var_2), colorMapper(var_3), colorMapper(var_4), colorMapper(var_5), colorMapper(var_6), colorMapper(var_7), colorMapper(var_8), colorMapper(var_9), + colorMapper(var_10), colorMapper(var_11), colorMapper(var_12)] + + weights = [weight_1.get(), weight_2.get(), weight_3.get(), weight_4.get(), weight_5.get(), weight_6.get(), weight_7.get(), weight_8.get(), weight_9.get(), weight_10.get(), weight_11.get(), + weight_12.get()] + labels = ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12'] + + index_value = float(final_score.get()) + + fig, ax = plt.subplots(figsize=(5, 3), dpi=150) + ax.clear() + ax.axis('equal') + radius = 1.0 + pie2 = ax.pie(weights, radius=radius, colors=colors, labeldistance=(radius * 0.875), labels=labels, + rotatelabels=False, startangle=90, counterclock=False, + wedgeprops={"edgecolor": "black", 'linewidth': 1}, textprops={'fontsize': (radius * 10)}) + + plt.setp(pie2[1], rotation_mode="anchor", ha="center", va="center") + for tx in pie2[1]: + rot = tx.get_rotation() + tx.set_rotation(rot) + # if you want rotated labels: + # tx.set_rotation(rot+ 90 + (1 - rot // 180) * 180) + + circle = plt.Circle(xy=(0, 0), radius=(radius * 0.75), facecolor=colorMapper(index_value), edgecolor='black', + linewidth=1) + plt.gca().add_artist(circle) -''' -Populate the tabs -''' -# --------------------------- TAB1 --------------------------- -tab1 = Tab(tab_no='1', title='Sample preparation placement', - text1='Procedures that avoid excessive sample transportation and ' - 'apply sample preparation that is integrated in analytical procedure are favoured', - text2='Sample preparation placement:', criterion=criteria[0]) -# create a frame to pack the entry into in order to get a border for the entry: -f1 = tk.Frame(tab1.frame, border=2, background=colors['accent']) -f1.grid(column=0, row=3, padx=8, pady=8, sticky='w') - -tab1_choices = {'In-line': 1.0, 'On-line': 0.66, 'At-line': 0.33, 'Off-line': 0.0} -dropdown1 = ttk.OptionMenu(tab1.frame, criteria[0].optionvar, criteria[0].optionvar.get(), *tab1_choices.keys(), - command=lambda x: change_dropdown(criteria[0], tab1_choices)) -# the ttk style for OptionMenu does not include a style for the tk dropdown menu, -# so this has to be configured separately: -dropdown1['menu'].config(background=colors['background'], - activeborderwidth=5, - activebackground=colors['accent'], - activeforeground=colors['text'], - foreground=colors['text']) - -dropdown1.grid(column=0, row=3, padx=8, pady=8, sticky='w') - -# --------------------------- TAB2 --------------------------- -tab2 = Tab(tab_no='2', title='Hazardous materials', - text1='The use of toxic materials, including the use of acids and bases ' - 'for derivatization and digestion, should be avoided.', - text2='Input mass [g] or volume [mL] of problematic substances:', - criterion=criteria[1]) - -label_tab2 = ttk.Label(tab2.frame, text='Include substances that are toxic via one of exposure pathways or ' - 'labeled as bioaccumulative or persistent.', - wraplength=280) -label_tab2.grid(column=0, row=4, sticky='w') - -# create a frame to pack the entry into in order to get a border for the entry: -f2 = tk.Frame(tab2.frame, border=2, background=colors['accent']) -f2.grid(column=0, row=3, padx=8, pady=8, sticky='w') -# create the entry and pack it in the frame -entry2 = ttk.Entry(f2, textvariable=criteria[1].valuevar, width=10, style='AGREE_2.0_theme') -entry2.pack() - - -def calc_crit_2(event=None): + ax.text(0.5, 0.5, str(index_value), + verticalalignment='center', horizontalalignment='center', + transform=ax.transAxes, + color='black', fontsize=(radius * 40)) + + # create the annotations which are displayed next to the graph + textstr = '\n'.join(( + r'1. Sample treatment', + r'2. Sample amount', + r'3. Device positioning', + r'4. Sample prep. stages', + r'5. Automation, miniaturization', + r'6. Derivatization', + r'7. Waste', + r'8. Analysis throughput', + r'9. Energy consumption', + r'10. Source of reagents', + r'11. Toxicity', + r"12. Operator's safety")) + + props = dict(facecolor='white', alpha=0.0) + + # display the text (1.05 means the object will appear to the right of the plot): + text = ax.text(1.05, 0.83, textstr, transform=ax.transAxes, fontsize=10, verticalalignment='top', bbox=props) + fig.tight_layout() # for exporting a compact figure + + # save the image: + ftypes = [('PNG file', '.png'), ('All files', '*')] + filename = filedialog.asksaveasfilename(filetypes=ftypes, defaultextension='.png') + # save the plot in the specified path; the 'tight' option removes the whitespace from around the figure: + plt.savefig(filename, bbox_inches='tight') + + +# ************************************** +# add the save annotated plot functionality to the file dropdown menu: +FileMenu.add_command(label='Generate annotated result of the assessment', command=annotatedPieChart) + + +# ************************************** + +# define a temporary function to test the printing of global variables: +def print_variables(): try: - if criteria[1].valuevar.get() == 'Input': - criteria[1].textvar.set(1.0) - elif float(criteria[1].valuevar.get()) >= 10: - criteria[1].textvar.set(0) - elif float(criteria[1].valuevar.get()) <= 0.01: - criteria[1].textvar.set(1.0) + print('var_1: ' + str(var_1)) + print('var_2: ' + str(var_2)) + print('var_3: ' + str(var_3)) + print('var_5: ' + str(var_5)) + print('var_6: ' + str(var_6)) + print('var_7: ' + str(var_7)) + print('var_8: ' + str(var_8)) + print('var_9: ' + str(var_9)) + print('var_10: ' + str(var_10)) + print('var_11: ' + str(var_11)) + print('var_12: ' + str(var_12)) + + updateStatusBar() + + except NameError: + tkinter.messagebox.showerror(title='Name error', + message='Please fill all the variables') + + +# generate tab captions and prompt captions: +def tab(tab_no, text1, text2): + Label(tab_no, text=text1, wraplength=300, justify=LEFT).grid(sticky='w', row=0, column=0, padx=8, pady=8) + Label(tab_no, text=text2, wraplength=300, justify=LEFT).grid(sticky='w', row=1, column=0, padx=8, pady=8) + + +# ***************************************************************************************************************** +# TAB 1 +# ***************************************************************************************************************** +content_1 = tab(tab1, text1='Direct analytical techniques should be applied to avoid sample treatment.', + text2='Select the sampling procedure:') + +# Create a Tkinter variable +var_1_text = StringVar(tab1) +# Dictionary with options +var_1_text_choices = {'Remote sensing without sample damage': 1.0, + 'Remote sensing with little physical damage': 0.95, + 'Non-invasive analysis': 0.9, + 'In-field sampling and direct analysis': 0.85, + 'In-field sampling and on-line analysis': 0.78, + 'On-line analysis': 0.70, + 'At-line analysis': 0.60, + 'Off-line analysis': 0.48, + 'External sample pre- and treatment and batch analysis (reduced number of steps)': 0.30, + 'External sample pre- and treatment and batch analysis (large number of steps)': 0.0} +var_1_text.set('select') + +dropDown_1 = OptionMenu(tab1, var_1_text, *var_1_text_choices.keys()) +dropDown_1.config(wraplength=250, bg='white', justify=LEFT, width=40, anchor='w') +dropDown_1.grid(sticky='w', row=2, column=0, padx=8, pady=8) + + +# on change dropdown value, get the dictionary value and modify the global variable +def change_dropdown_1(*args): + # Define the global variable for Principle 1: + global var_1 + var_1 = None + if var_1_text.get() == 'select': + var_1 = 1.0 + else: + var_1 = var_1_text_choices[var_1_text.get()] + print('var_1:' + str(var_1)) + + chartPlotter() + + +# link function to change dropdown +# The trace method of the StringVar allows to detect the change in the variable that activate a call to a function +var_1_text.trace('w', change_dropdown_1) + +W_1 = weightChoice(10, 0, tab1, weight_1) + +# ***************************************************************************************************************** +# TAB 2 +# ***************************************************************************************************************** + +content_2 = tab(tab2, text1='Minimal sample size and minimal number of samples are goals.', + text2='Enter the amount of sample in either g or mL:') + +amount_var = StringVar() +amount_var.set('input') +sample_amount_entry = ttk.Entry(tab2, textvariable=amount_var, width=15) +sample_amount_entry.grid(sticky='w', row=2, column=0, padx=8, pady=8) + + +# the event=None is passed so that the entry.bind does not return a positional argument +def change_entry_2(event=None): + global var_2 + var_2 = None + + try: + if float(amount_var.get()) > 100: + var_2 = 0 + elif float(amount_var.get()) < 0.1: + var_2 = 1.0 else: - criteria[1].textvar.set( - abs(-0.145 * log(float(criteria[1].valuevar.get())) + 0.3333)) + var_2 = abs(-0.142 * log(float(amount_var.get())) + 0.65) # absolute value to avoid negative values + print('var_2:' + str(var_2)) + except ValueError: - messagebox.showerror(title='Value error', - message='The amount has to be a float or an integer, e.g. 0.14 or 21.') - create_plot() - print('criterion 2: ', criteria[1].textvar.get()) - - -entry2.bind('<Return>', calc_crit_2) -ttk.Button(tab2.frame, text='Set', command=calc_crit_2, width=8).grid(column=0, row=3, padx=80, pady=8, sticky='w') - -# --------------------------- TAB3 --------------------------- -tab3 = Tab(tab_no='3', title='Sustainability and renewability of materials', - text1='The application of materials from sustainable or renewable sources ' - 'should be prioritized.', - text2='Ratio of the mass of sustainable and renewable materials to the total ' - 'mass of materials used:', criterion=criteria[2]) - -tab3_choices = {'Only sustainable and renewable materials are used several times': 1.0, - '> 75% of reagents and materials are sustainable or renewable': 0.75, - '50-75% of reagents and materials are sustainable or renewable and can only be used once': 0.50, - '25-50% of reagents and materials are sustainable or renewable': 0.25, - '< 25% of reagents and materials are sustainable or renewable and can only be used once': 0, - 'Materials are not sustainable or renewable and are used several times': 0.5,} -dropdown3 = ttk.OptionMenu(tab3.frame, criteria[2].optionvar, criteria[2].optionvar.get(), *tab3_choices.keys(), - command=lambda x: change_dropdown(criteria[2], tab3_choices)) -# the ttk style for OptionMenu does not include a style for the tk dropdown menu, -# so this has to be configured separately: -dropdown3['menu'].config(background=colors['background'], - activeborderwidth=5, - activebackground=colors['accent'], - activeforeground=colors['text'], - foreground=colors['text']) - -dropdown3.grid(column=0, row=3, padx=8, pady=8, sticky='w') - -# --------------------------- TAB4 --------------------------- -tab4 = Tab(tab_no='4', title='Waste', - text1='In analytical chemistry, all material inputs, including ' - 'the sample preparation stage, can be treated as waste.', - text2='Input mass [g] or volume [mL] of waste:', criterion=criteria[3]) -# create a frame to pack the entry into in order to get a border for the entry: -f4 = tk.Frame(tab4.frame, border=2, background=colors['accent']) -f4.grid(column=0, row=3, padx=8, pady=8, sticky='w') -# create the entry and pack it in the frame -entry4 = ttk.Entry(f4, textvariable=criteria[3].valuevar, width=10, style='AGREE_2.0_theme') -entry4.pack() - - -def calc_crit_4(event=None): + tkinter.messagebox.showerror(title='Value error', message='The amount has to be a float or an integer, e.g. 0.14 or 21.') + + chartPlotter() + + +# bind the <Return> key to the entry window, so that the function gets called as an alternative to the 'set' button: +sample_amount_entry.bind('<Return>', change_entry_2) +# insert a button that does the same: +ttk.Button(tab2, text='Set', command=change_entry_2).grid(row=2, column=0, padx=8, pady=8) + +W_2 = weightChoice(10, 0, tab2, weight_2) + +# ***************************************************************************************************************** +# TAB 3 +# ***************************************************************************************************************** + +content_3 = tab(tab3, 'If possible, measurements should be performed in situ.', + 'What is the positioning of the analytical device?') + +# Create a Tkinter variable +var_3_text = StringVar(tab3) +# Dictionary with options +var_3_text_choices = {'off-line': 0.0, + 'at-line': 0.33, + 'on-line': 0.66, + 'in-line': 1.0} + +var_3_text.set('select') + +dropDown_3 = OptionMenu(tab3, var_3_text, *var_3_text_choices.keys()) +dropDown_3.config(wraplength=250, bg='white', justify=LEFT, width=40, anchor='w') +dropDown_3.grid(sticky='w', row=2, column=0, padx=8, pady=8) + + +# on change dropdown value, get the dictionary value and modify the global variable +def change_dropdown_3(*args): + global var_3 + var_3 = None + if var_3_text.get() == 'select': + var_3 = 1.0 + else: + var_3 = var_3_text_choices[var_3_text.get()] + print('var_3:' + str(var_3)) + chartPlotter() + + +# link function to change dropdown +# The trace method of the StringVar allows to detect the change in the variable that activate a call to a function +var_3_text.trace('w', change_dropdown_3) + +W_3 = weightChoice(10, 0, tab3, weight_3) + +# *************************** TAB 4 ************************************************************************************ +content_4 = tab(tab4, text1='Integration of analytical processes and operations saves energy and reduces the use of reagents.', + text2='How many major, distinct steps are there in the sample preparation procedure? These include e.g. sonication,' + ' mineralization, centrifugation, derivatization, extraction, etc.') + +var_4_text = StringVar(tab4) +# Dictionary with options +var_4_text_choices = {'3 or fewer': 1.0, + '4': 0.8, + '5': 0.6, + '6': 0.4, + '7': 0.2, + '8 or more': 0.0} + +var_4_text.set('select') + +dropDown_4 = OptionMenu(tab4, var_4_text, *var_4_text_choices.keys()) +dropDown_4.config(wraplength=250, bg='white', justify=LEFT, width=40, anchor='w') +dropDown_4.grid(sticky='w', row=2, column=0, padx=8, pady=8) + + +# on change dropdown value, get the dictionary value and modify the global variable +def change_dropdown_4(*args): + global var_4 + var_4 = None + if var_4_text.get() == 'select': + var_4 = 1.0 + else: + var_4 = var_4_text_choices[var_4_text.get()] + print('var_4:' + str(var_4)) + chartPlotter() + + +# link function to change dropdown +# The trace method of the StringVar allows to detect the change in the variable that activate a call to a function +var_4_text.trace('w', change_dropdown_4) + +W_4 = weightChoice(10, 0, tab4, weight_4) + +# ***************************************************************************************************************** +# TAB 5 +# ***************************************************************************************************************** +content_5 = tab(tab5, text1='Automated and miniaturized methods should be selected.', text2='Degree of automation:') + +# Create a Tkinter variable +var_5a_text = StringVar(tab5) +# Dictionary with options +var_5a_text_choices = {'automatic': 1.0, + 'semi-automatic': 0.5, + 'manual': 0.0} + +var_5a_text.set('select') + +dropDown_5a = OptionMenu(tab5, var_5a_text, *var_5a_text_choices.keys()) +dropDown_5a.config(wraplength=250, bg='white', justify=LEFT, width=40, anchor='w') +dropDown_5a.grid(sticky='w', row=2, column=0, padx=8, pady=8) + +var_5a = DoubleVar(tab5) +var_5a.set(1.0) +var_5b = DoubleVar(tab5) +var_5b.set(1.0) + + +def variableFive(): + global var_5 + a = var_5a.get() + b = var_5b.get() + if a == 1.0 and b == 1.0: + var_5 = 1.0 + elif a == 0.5 and b == 1.0: + var_5 = 0.75 + elif a == 0.0 and b == 1.0: + var_5 = 0.5 + elif a == 1.0 and b == 0.0: + var_5 = 0.5 + elif a == 0.5 and b == 0.0: + var_5 = 0.25 + elif a == 0.0 and b == 0.0: + var_5 = 0.0 + + +# on change dropdown value, get the dictionary value and modify the global variable +def change_dropdown_5a(*args): + + if var_5a_text.get() == 'select': + var_5a.set(1.0) + else: + var_5a.set(var_5a_text_choices[var_5a_text.get()]) + print('var_5a:' + str(var_5a)) + variableFive() + + chartPlotter() + + +# link function to change dropdown +# The trace method of the StringVar allows to detect the change in the variable that activate a call to a function +var_5a_text.trace('w', change_dropdown_5a) + +Label(tab5, text='Sample preparation:', wraplength=300, justify=LEFT).grid(sticky='w', row=3, column=0, padx=8, pady=8) + +var_5b_text = StringVar(tab5) +var_5b_text_choices = {'none or miniaturized': 1.0, + 'not miniaturized': 0.0} +var_5b_text.set('select') +dropDown_5b = OptionMenu(tab5, var_5b_text, *var_5b_text_choices.keys()) +dropDown_5b.config(wraplength=250, bg='white', justify=LEFT, width=40, anchor='w') +dropDown_5b.grid(sticky='w', row=4, column=0, padx=8, pady=8) + + +def change_dropdown_5b(*args): + if var_5b_text.get() == 'select': + var_5b.set(1.0) + else: + var_5b.set(var_5b_text_choices[var_5b_text.get()]) + variableFive() + print('var_5:' + str(var_5)) + + chartPlotter() + + +var_5b_text.trace('w', change_dropdown_5b) + +W_5 = weightChoice(10, 0, tab5, weight_5) + +# ***************************************************************************************************************** +# TAB 6 +# ***************************************************************************************************************** +content_6 = tab(tab6, text1='Derivatization should be avoided.', text2='Select derivatization agents (if used):') + + +# combine the selected options into a single string to produce a label caption +def concatenate_text(list_): + caption = '' + for i in list_: + caption = caption + i + '; ' + return caption + + +def Select(): + reslist = list() + selecion = lbox.curselection() + + for i in selecion: + entered = lbox.get(i) + reslist.append(entered) + global_list.append(entered) + + # update the label box with selected deriv. agents: + print(reslist) + print(global_list) + v.set(concatenate_text(global_list)) + + global var_6 + var_6 = 1.0 + + # add a -0.2 penaly for using derivatization agents: + for CAS in global_list: + var_6 = var_6 * lbox_list[CAS] + + if var_6 > 0.2: + var_6 = var_6 - 0.2 + else: + var_6 = 0.0 + + print(var_6) + chartPlotter() + + +# update the list box +def update_list(*args): + search_term = search_var.get() + + lbox.delete(0, END) + + for item in lbox_list.keys(): + if search_term.lower() in item.lower(): + lbox.insert(END, item) + + +# clear the selection and the displayed label caption +def clear_list(): + global v, global_list + v.set('') + global var_6 + var_6 = 1.0 + global_list = [] + chartPlotter() + + +# create global variables +global_list = [] +lbox_list = { + # "None": 1.1, + "5950-69-6": 1, + "30084-90-3": 1, + "12093-10-6": 1, + "6283-74-5": 1, + "119-53-9": 1, + "126-81-8": 1, + "24257-93-0": 1, + "58068-85-2": 0.949158975023003, + "1273-85-4": 0.949158975023003, + "4233-33-4": 0.949158975023003, + "100-10-7": 0.949158975023003, + "38183-12-9": 0.945068038553621, + "41468-25-1": 0.937140592655187, + "1395920-13-4": 0.937140592655187, + "521-24-4": 0.937140592655187, + "56606-21-4": 0.935584744799731, + "65-22-5": 0.925393321432741, + "68123-33-1": 0.925393321432741, + "913253-56-2": 0.913914155272091, + "124522-09-4": 0.913914155272091, + "223463-14-7": 0.902699986612441, + "1118-68-9": 0.901394170230429, + "952102-12-4": 0.901394170230429, + "536-69-6": 0.901394170230429, + "203256-20-6": 0.901394170230429, + "86516-36-1": 0.899210326049394, + "861881-76-7": 0.886368566581839, + "56139-74-3": 0.869932201280637, + "84806-27-9": 0.865490140567591, + "91366-65-3": 0.865490140567591, + "67229-93-0": 0.855427480281241, + "1273-82-1": 0.855042238169516, + "50632-57-0": 0.846792397075292, + "10199-89-0": 0.839008465483774, + "152111-91-6": 0.836037308222637, + "7149-49-7": 0.830362674910287, + "3029-19-4": 0.830362674910287, + "68572-87-2": 0.829473879117877, + "12152-94-2": 0.829473879117877, + "29270-56-2": 0.829154698457689, + "24463-19-2": 0.827803622060042, + "100-39-0": 0.825375773537705, + "550-44-7": 0.822230968349539, + " 49759-20-8": 0.822230968349539, + "38609-97-1": 0.822230968349539, + "35661-51-9": 0.822230968349539, + "10401-59-9": 0.822230968349539, + "70402-14-1": 0.822230968349539, + "131076-14-7": 0.822230968349539, + "214147-22-5": 0.822230968349539, + "4930-98-7": 0.822230968349539, + "569355-30-2": 0.822230968349539, + "53348-04-2": 0.820406195102248, + "67580-39-6": 0.818423316626862, + "68133-98-2": 0.814016502590708, + "81864-15-5": 0.814016502590708, + "113722-81-9": 0.814016502590708, + "15537-71-0": 0.809079828950995, + "33008-06-9": 0.809079828950995, + "139332-64-2": 0.809079828950995, + "62642-61-9": 0.806764775754175, + "100139-54-6": 0.806764775754175, + "62796-29-6": 0.797901423240715, + "87-13-8": 0.783298381421747, + "35231-44-8": 0.778837259389339, + "88404-25-5": 0.778837259389339, + "485-47-2": 0.77674392680131, + "58520-45-9": 0.776282830117383, + "107-91-5": 0.776282830117383, + "139332-66-4": 0.776282830117383, + "89-25-8": 0.776282830117383, + "18428-76-7": 0.776282830117383, + "20624-25-3": 0.763216179776723, + "27072-45-3": 0.762516465156704, + "1459205-36-7": 0.755628677634781, + "96483-68-0": 0.747181595887401, + "132098-76-1": 0.747181595887401, + "98-59-9": 0.746227267824334, + "7612-98-8": 0.744246233476037, + "5415-58-7": 0.742560985030801, + "76-83-5": 0.740506239181083, + "1293-79-4": 0.740506239181083, + "28920-43-6": 0.740506239181083, + "100-07-2": 0.740506239181083, + "99-73-0": 0.738962425018157, + "22265-37-8": 0.737084384687495, + "3731-51-9": 0.737084384687495, + "141903-34-6": 0.737084384687495, + "122-04-3": 0.732376041854033, + "4755-50-4": 0.732376041854033, + "99-33-2": 0.732376041854033, + "605-65-2": 0.723192330411814, + "56512-49-3": 0.723192330411814, + "126565-42-2": 0.723192330411814, + "7693-46-1": 0.721322673837572, + "1711-06-4": 0.717883414280986, + "93128-04-2": 0.717798274857161, + "613-54-7": 0.716357636495872, + "74367-78-5": 0.710065827927279, + "119-26-6": 0.692633685424727, + "2508-19-2": 0.692425832968952, + "21614-17-5": 0.682522312223409, + "80-11-5": 0.681782236352849, + "100-46-9": 0.679263084173718, + "55486-13-0": 0.666338980106273, + "16315-59-6": 0.665281844920184, + "5102-79-4": 0.664748970983542, + "70-34-8": 0.664086673111964, + "132-32-1": 0.659883743356088, + "36410-81-8": 0.659179085176979, + "100-16-3": 0.659159320154698, + "104077-15-8": 0.659091847163412, + "4083-64-1": 0.649947842697737, + "21324-39-0": 0.634865149902982, + "2978-11-2_": 0.629540812510628, + "456-27-9": 0.628988106517093, + "98-09-9": 0.628032387327697, + "103-72-0": 0.606674230911606, + "504-29-0": 0.587444277328904, + "86-84-0": 0.566544585073271, + "36877-69-7": 0.556132009449506, + "103-71-9": 0.525453097624119, + "551-06-4": 0.510591749035237, + "643-79-8": 0.486298449205041, + "98-88-4": 0.475562851988167, + "5470-11-1": 0.466906948575218, + "99-65-0": 0.414382740812551, + "95-54-5": 0.409876625997181, + "60-24-2": 0.380580959884422, + "1118-71-4": 1, + "4426-47-5": 0.98287765750619, + "35342-88-2": 0.934408589712128, + "13435-12-6": 0.934408589712128, + "122-51-0": 0.90808769546171, + "17455-13-9": 0.898290310316299, + "7449-74-3": 0.896162794934563, + "1188-33-6": 0.873968193624155, + "1133-63-7": 0.845047181007906, + "57981-02-9": 0.843544327015115, + "3449-26-1": 0.831289869514086, + "54925-64-3": 0.831289869514086, + "7453-26-1": 0.831289869514086, + "23231-91-6": 0.82477424558194, + "423-39-2": 0.821174784952006, + "3332-29-4": 0.817379220173597, + "18297-63-7": 0.804205531712304, + "13257-81-3": 0.796997494513717, + "73980-71-9": 0.796226219175859, + "828-73-9": 0.796226219175859, + "36805-97-7": 0.785921382127458, + "6006-65-1": 0.785921382127458, + "4909-78-8": 0.785921382127458, + "920-68-3": 0.785921382127458, + "653-37-2": 0.78349900067157, + "422-05-9": 0.78349900067157, + "2182-66-3": 0.766534069464941, + "354-64-3": 0.763789874990475, + "58479-61-1": 0.763598909336104, + "13154-24-0": 0.763598909336104, + "70-11-1": 0.761090045768687, + "723336-86-5": 0.761090045768687, + "850418-19-8": 0.761090045768687, + "850418-20-1": 0.761090045768687, + "1546-79-8": 0.758242430472499, + "24589-78-4": 0.758242430472499, + "53296-64-3": 0.758242430472499, + "77377-52-7": 0.758242430472499, + "82112-21-8": 0.757927402509425, + "375-22-4": 0.756114760094685, + "336-59-4": 0.756114760094685, + "356-42-3": 0.756114760094685, + "420-37-1": 0.75205982910284, + "77-76-9": 0.750711985826051, + "20082-71-7": 0.749832128609721, + "2251-50-5": 0.747481224283863, + "100-11-8": 0.745015119615777, + "18162-48-6": 0.743146183479067, + "425-75-2": 0.742441949845756, + "1765-40-8": 0.742441949845756, + "76437-40-6": 0.742441949845756, + "80522-42-5": 0.742441949845756, + "1538-75-6": 0.74152936540163, + "98-03-3": 0.739537905180287, + "87020-42-6": 0.737007165264001, + "589-15-1": 0.736264650708209, + "2857-97-8": 0.736016815715654, + "17950-40-2": 0.732111366794642, + "407-25-0": 0.731258587142799, + "115-20-8": 0.730613289210088, + "823-96-1": 0.721670319376414, + "71735-32-5": 0.7183910746808, + "333-27-7": 0.7183910746808, + "996-50-9": 0.714539433160182, + "3768-58-9": 0.714539433160182, + "685-27-8": 0.713300737795531, + "25561-30-2": 0.713300737795531, + "124-41-4": 0.70689269806413, + "15933-59-2": 0.705803556150421, + "18156-74-6": 0.705803556150421, + "123-62-6": 0.703483768736821, + "2083-91-2": 0.703043095426246, + "10416-59-8": 0.700353286433786, + "69739-34-0": 0.696757084764058, + "107-46-0": 0.696026303459663, + "541-88-8": 0.680085578563036, + "994-30-9": 0.659639561940176, + "75-26-3": 0.65077439166517, + "543-27-1": 0.643008761928377, + "6092-54-2": 0.619827404668639, + "76-02-8": 0.618803077595292, + "75-77-4": 0.606190113014358, + "7719-09-7": 0.598432942089881, + "1066-35-9": 0.590259358282054, + "4637-24-5": 0.587695662266982, + "920-66-1": 0.5835440122017, + "8077-35-8": 0.580905093441462, + "108-24-7": 0.56539851162607, + "10294-34-5": 0.546920496297807, + "999-97-3": 0.539120875551113, + "7637-07-2": 0.536295783559384, + "75-89-8": 0.517064147633066, + "1899-02-1": 0.453968334570473, + "17287-03-5": 0.450591161239778, + "7664-93-9": 0.430740368201206, + "132228-87-6": 0.389860157052623, + "75-59-2": 0.35207841911058, + "77-78-1": 0.185707987424391, + "19132-06-0": 1, + "1052236-86-8": 1, + "135806-59-6": 1, + "139658-04-1": 1, + "108031-79-4": 1, + "124529-02-8": 0.789788397239459, + "124529-07-3": 0.789788397239459, + "24277-43-8": 0.789788397239459, + "958300-06-6": 0.789788397239459, + "5978-70-1": 0.661143997568766, + "3886-70-2": 0.62276366189702, + "20445-31-2": 0.616318224518582, + "17257-71-5": 0.616318224518582, + "81655-41-6": 0.616318224518582, + "21451-74-1": 0.616318224518582, + "14645-24-0": 0.616318224518582, + "147948-52-5": 0.581990910059596, + "104371-20-2": 0.581990910059596, + "132679-61-9": 0.56145194750795, + "210529-62-7": 0.56145194750795, + "3347-90-8": 0.550846501071722, + "104530-16-7": 0.547959104197752, + "39637-74-6": 0.547959104197752, + "39262-22-1": 0.52022184149657, + "1517-69-7": 0.474716248097616, + "1445-91-6": 0.474716248097616, + "107474-79-3": 0.437963083473382, + "14602-86-9": 0.412055011328408, + "3886-69-9": 0.358144912356212, + "2627-86-3": 0.326740839342668, + "24277-44-9": 0.288185973785988, + "62414-75-9": 0.288185973785988, + "14152-97-7": 0.288185973785988, + "42340-98-7": 0.176714727821325, + "14649-03-7": 0.132441393121765, + "33375-06-3": 0.116078677380125, +} +v = StringVar() +chckbxVar_tab6 = StringVar() +chckbxVar_tab6.set('disabled') + +# set initial blank value of the StringVar +v.set('') + +search_var = StringVar() +search_var.trace("w", update_list) +entry = ttk.Entry(tab6, textvariable=search_var, width=13) +# disable the lookup box initially: +# entry.config(state=DISABLED) + +scrollbar = ttk.Scrollbar(tab6, orient='vertical') +scrollbar.grid(row=3, column=0, sticky='w', ipady=30, padx=(220, 0)) +lbox = Listbox(tab6, width=34, height=6, yscrollcommand=scrollbar.set) # selectmode=MULTIPLE +# disable the lbox initially: +# lbox.config(state=DISABLED) + +# def lboxActivator(): +# +# # chckbxVar_tab6.set('disabled') +# if chckbxVar_tab6.get() == 'enabled': +# entry.config(state=ACTIVE) +# lbox.config(state=NORMAL) +# elif chckbxVar_tab6.get() == 'disabled': +# entry.config(state=DISABLED) +# lbox.config(state=DISABLED) +# +# ttk.Checkbutton(tab6, text='Derivatisation agent is used', command=lboxActivator, variable=chckbxVar_tab6, onvalue='enabled', offvalue='disabled').grid(row=30, column=0) + +# lboxActivator() + +Label(tab6, text='CAS lookup: ').grid(row=2, column=0, padx=8, pady=3, sticky='w') +entry.grid(row=2, column=0, padx=(100, 0), pady=3, sticky='w') +lbox.grid(row=3, column=0, padx=8, pady=3, sticky='w') + +# link the scrollbar to the list box +scrollbar.config(command=lbox.yview) + +ttk.Button(tab6, text="Select", command=Select, width=8).grid(column=1, row=3, padx=4) +# clear the selection and the caption +ttk.Button(tab6, text='Clear', command=lambda: [clear_list(), update_list()], width=8).grid(column=1, row=4, padx=4) + +Label(tab6, text='Selected CAS: ').grid(column=0, row=4, sticky='w', padx=8, pady=0) +ttk.Label(tab6, textvariable=v, wraplength=180, width=34, relief='groove').grid(column=0, row=5, sticky='w', padx=8, pady=4) + +# call the function to populate the list at the beginning +update_list() + +W_6 = weightChoice(10, 0, tab6, weight_6) + +# ***************************************************************************************************************** +# TAB 7 +# ***************************************************************************************************************** +content_7 = tab(tab7, text1='Generation of a large volume of analytical waste should be avoided, and proper management ' + 'of analytical waste should be provided.', text2='Enter the amount of waste in g or mL:') + +amount_var7 = StringVar() +amount_var7.set('input') + + +# the event=None is passed so that the entry.bind does not return a positional argument +def change_entry_7(event=None): + global var_7 + var_7 = None + try: - if criteria[3].valuevar.get() == 'Input': - criteria[3].textvar.set(1.0) - elif float(criteria[3].valuevar.get()) >= 50: - criteria[3].textvar.set(0) - elif float(criteria[3].valuevar.get()) <= 0.1: - criteria[3].textvar.set(1.0) + if float(amount_var7.get()) > 150: + var_7 = 0 + elif float(amount_var7.get()) < 0.1: + var_7 = 1.0 else: - criteria[3].textvar.set( - abs(-0.161 * log(float(criteria[3].valuevar.get())) + 0.6295)) + var_7 = abs(-0.134 * log(float(amount_var7.get())) + 0.6946) # absolute value to avoid negative values + print('var_7:' + str(var_7)) + except ValueError: - messagebox.showerror(title='Value error', - message='The amount has to be a float or an integer, e.g. 0.14 or 21.') - create_plot() - print('Criterion 4: ', criteria[3].textvar.get()) + tkinter.messagebox.showerror(title='Value error', message='The amount has to be a float or an integer, e.g. 0.14 or 21.') + + chartPlotter() + +sample_amount_entry7 = ttk.Entry(tab7, textvariable=amount_var7, width=15) +sample_amount_entry7.grid(sticky='w', row=2, column=0, padx=8, pady=8) -entry4.bind('<Return>', calc_crit_4) -ttk.Button(tab4.frame, text='Set', command=calc_crit_4, width=8).grid(column=0, row=3, padx=80, pady=8, sticky='w') +# bind the <Return> key to the entry window, so that the function gets called as an alternative to the 'set' button: +sample_amount_entry7.bind('<Return>', change_entry_7) +# insert a button that does the same: +ttk.Button(tab7, text='Set', command=change_entry_7).grid(row=2, column=0, padx=8, pady=8) -# --------------------------- TAB5 --------------------------- -tab5 = Tab(tab_no='5', title='Size economy of the sample', - text1='Smaller sample sizes should be favoured, as long as sample representativeness is assured', - text2='Input mass [g] or volume [mL] of the sample:', - criterion=criteria[4]) +W_7 = weightChoice(10, 0, tab7, weight_7) -f5 = tk.Frame(tab5.frame, border=2, background=colors['accent']) -f5.grid(column=0, row=3, padx=8, pady=8, sticky='w') -entry5 = ttk.Entry(f5, textvariable=criteria[4].valuevar, width=10, style='AGREE_2.0_theme') -entry5.pack() +# ***************************************************************************************************************** +# TAB 8 +# ***************************************************************************************************************** +content_8 = tab(tab8, text1='Multi-analyte or multi-parameter methods are preferred ' + 'versus methods using one analyte at a time.', + text2='Number of analytes determined in a single run:') +amount_var8a = StringVar() +amount_var8a.set('input') +sample_amount_entry8a = ttk.Entry(tab8, textvariable=amount_var8a, width=15) +sample_amount_entry8a.grid(sticky='w', row=2, column=0, padx=8, pady=8) -def calc_crit_5(event=None): +Label(tab8, text='Sample throughput (samples analysed per hour):', wraplength=300, justify=LEFT).grid(sticky='w', row=3, column=0, padx=8, pady=8) + +amount_var8b = StringVar() +amount_var8b.set('input') +sample_amount_entry8b = ttk.Entry(tab8, textvariable=amount_var8b, width=15) +sample_amount_entry8b.grid(sticky='w', row=4, column=0, padx=8, pady=8) + + +def change_entry_8(event=None): + global var_8 + var_8 = None + + # only integers make sense in this case: try: - if criteria[4].valuevar.get() == 'Input': - criteria[4].textvar.set(1.0) - elif float(criteria[4].valuevar.get()) >= 100: - criteria[4].textvar.set(0) - elif float(criteria[4].valuevar.get()) <= 0.1: - criteria[4].textvar.set(1.0) + if float(amount_var8a.get()).is_integer(): + try: + + if (float(amount_var8a.get()) * float(amount_var8b.get())) < 1.0: + var_8 = 0.0 + elif (float(amount_var8a.get()) * float(amount_var8b.get())) > 70.0: + var_8 = 1.0 + else: + var_8 = abs(0.2429 * log(float(amount_var8a.get()) * float(amount_var8b.get())) - 0.0517) # absolute value to avoid negative values + print('var_8:' + str(var_8)) + + except ValueError: + tkinter.messagebox.showerror(title='Value error', + message='The number of analytes has to be an integer, e.g. 2 or 21, and the sample throughput has to be a float or an integer, e.g. 2.1 or 3.') else: - criteria[4].textvar.set( - abs(-0.145 * log(float(criteria[4].valuevar.get())) + 0.6667)) + tkinter.messagebox.showerror(title='Value error', message='The number of analytes has to be an integer, e.g. 2 or 21.') except ValueError: - messagebox.showerror(title='Value error', - message='The amount has to be a float or an integer, e.g. 0.14 or 21.') - create_plot() + tkinter.messagebox.showerror(title='Value error', + message='The number of analytes has to be an integer, e.g. 2 or 21, and the sample throughput has to be a float or an integer, e.g. 2.1 or 3.') + # refresh the plot: + chartPlotter() + + +sample_amount_entry8b.bind('<Return>', change_entry_8) +ttk.Button(tab8, text='Set', command=change_entry_8).grid(row=5, column=0, padx=8, pady=8) + +W_8 = weightChoice(10, 0, tab8, weight_8) + +# ***************************************************************************************************************** +# TAB 9 +# ***************************************************************************************************************** +content_9 = tab(tab9, text1='The use of energy should be minimized.', + text2='Select the most energy-intensive technique used in the method:') + +var_9_text = StringVar(tab9) +amount_var9 = StringVar(tab9) +amount_var9.set('input') +# Dictionary with options +var_9_text_choices = {'None': 1.0, + 'FTIR': 1.0, + 'Hot plate solvent evaporation (<10 min)': 1.0, + 'Rotary evaporation': 1.0, + 'Needle evaporation': 1.0, + 'Ultrasound-assisted extraction': 1.0, + 'SPE and SPME': 1.0, + 'Microbiological assays': 1.0, + 'Immunoassay': 1.0, + 'Spectrofluorometry': 1.0, + 'Titration': 1.0, + 'UPLC': 1.0, + 'UV-Vis Spectrometry': 1.0, + 'Energy dispersive X-ray fluorescence': 1.0, + 'Potentiometry': 1.0, + 'Non-instrumental detection': 1.0, + + 'Hot plate solvent evaporation (10-150 min)': 0.5, + 'Accelerated solvent extraction': 0.5, + 'Supercritical fluid extraction': 0.5, + 'Microwave assisted extraction': 0.5, + 'Flame atomic absorption spectrometry': 0.5, + 'Electrothermal atomic absorption spectrometry': 0.5, + 'GC': 0.5, + 'ICP-MS': 0.5, + 'ICP-OES': 0.5, + 'LC': 0.5, + + 'Hot plate solvent evaporation (>150 min)': 0.0, + 'Soxhlet extraction': 0.0, + 'NMR': 0.0, + 'GC-MS': 0.0, + 'LC-MS': 0.0, + 'X-ray diffractometry': 0.0} + +var_9_text.set('select') + +dropDown_9 = OptionMenu(tab9, var_9_text, *var_9_text_choices.keys()) +dropDown_9.config(wraplength=250, bg='white', justify=LEFT, width=40, anchor='w') +dropDown_9.grid(sticky='w', row=2, column=0, padx=8, pady=8) + + +def change_dropdown_9(*args): + global var_9 + var_9 = 1.0 + if var_9_text.get() == 'select': + var_9 = 1.0 + else: + var_9 = var_9_text_choices[var_9_text.get()] + print('var_9:' + str(var_9)) + chartPlotter() -entry5.bind('<Return>', calc_crit_5) -ttk.Button(tab5.frame, text='Set', command=calc_crit_5, width=8).grid(column=0, row=3, padx=80, pady=8, sticky='w') +var_9_text.trace('w', change_dropdown_9) -# --------------------------- TAB6 --------------------------- -tab6 = Tab(tab_no='6', title='Sample throughput', - text1='Sample throughput determines the overall duration of the sample preparation stage.', - text2='Input the number of samples that can be prepared in one hour:', - criterion=criteria[5]) +ttk.Label(tab9, text='Alternatively, estimate the total power consumption of a single analysis in kWh:', wraplength=250, justify=LEFT).grid(sticky='w', row=3, column=0, padx=8, pady=8) -f6 = tk.Frame(tab6.frame, border=2, background=colors['accent']) -f6.grid(column=0, row=3, padx=8, pady=8, sticky='w') -entry6 = ttk.Entry(f6, textvariable=criteria[5].valuevar, width=10, style='AGREE_2.0_theme') -entry6.pack() +sample_amount_entry9 = ttk.Entry(tab9, textvariable=amount_var9, width=15) +sample_amount_entry9.grid(sticky='w', row=4, column=0, padx=8, pady=8) -def calc_crit_6(event=None): +def change_entry_9(event=None): + global var_9 + var_9 = 1.0 + try: - if criteria[5].valuevar.get() == 'Input': - criteria[5].textvar.set(1.0) - elif float(criteria[5].valuevar.get()) >= 70: - criteria[5].textvar.set(1.0) - elif float(criteria[5].valuevar.get()) <= 1.0: - criteria[5].textvar.set(0.0) + if float(amount_var9.get()) > 1.5: + var_9 = 0.0 + elif float(amount_var9.get()) < 0.1: + var_9 = 1.0 else: - criteria[5].textvar.set( - abs(0.2354 * log(float(criteria[5].valuevar.get())))) + var_9 = abs(-0.7143 * (float(amount_var9.get())) + 1.0714) # absolute value to avoid negative values + print('var_9:' + str(var_9)) + except ValueError: - messagebox.showerror(title='Value error', - message='The amount has to be a float or an integer, e.g. 0.14 or 21.') - create_plot() + tkinter.messagebox.showerror(title='Value error', message='The amount has to be a float or an integer, e.g. 0.14 or 21.') + + chartPlotter() + + +sample_amount_entry9.bind('<Return>', change_entry_9) +ttk.Button(tab9, text='Set', command=change_entry_9).grid(row=4, column=0, padx=8, pady=8) +W_9 = weightChoice(10, 0, tab9, weight_9) +# ***************************************************************************************************************** +# TAB 10 +# ***************************************************************************************************************** +content_10 = tab(tab10, text1='Reagents obtained from renewable sources should be preferred.', + text2='Select the type of reagents:') -entry6.bind('<Return>', calc_crit_6) -ttk.Button(tab6.frame, text='Set', command=calc_crit_6, width=8).grid(column=0, row=3, padx=80, pady=8, sticky='w') +var_10_text = StringVar(tab10) +# Dictionary with options +var_10_text_choices = {'No reagents': 1.0, + 'All reagents are bio-based': 1.0, + 'Some reagents are bio-based': 0.5, + 'None of the reagents are from bio-based sources': 0.0 + } -# --------------------------- TAB7 --------------------------- -tab7 = Tab(tab_no='7', title='Integration and automation', - text1='Sample preparation should be simplified and automated if possible.', - text2='1. Select the number of discrete steps in the sample preparation procedure:', - criterion=criteria[6]) +var_10_text.set('select') -tab7a_choices = {'2 steps or fewer': 1.0, - '3 steps': 0.75, - '4 steps': 0.50, - '5 steps': 0.25, - '6 steps or more': 0} +dropDown_10 = OptionMenu(tab10, var_10_text, *var_10_text_choices.keys()) +dropDown_10.config(wraplength=250, bg='white', justify=LEFT, width=40, anchor='w') +dropDown_10.grid(sticky='w', row=2, column=0, padx=8, pady=8) -def calc_crit_7(event=None): - # create temporary variables for each dropdown: - a = tk.StringVar() - b = tk.StringVar() - # reset the temp variables to default if the global reset was triggered: - a.set('1.0') - b.set('1.0') +def change_dropdown_10(*args): + global var_10 + var_10 = None + if var_10_text.get() == 'select': + var_10 = 1.0 + else: + var_10 = var_10_text_choices[var_10_text.get()] + print('var_10:' + str(var_10)) + chartPlotter() + - if criteria[6].optionvar_a.get() != 'Select': - a.set(tab7a_choices[criteria[6].optionvar_a.get()]) +var_10_text.trace('w', change_dropdown_10) - if criteria[6].optionvar_b.get() != 'Select': - b.set(tab7b_choices[criteria[6].optionvar_b.get()]) +W_10 = weightChoice(10, 0, tab10, weight_10) - criteria[6].textvar.set(str(float(a.get()) * float(b.get()))) - create_plot() +# ***************************************************************************************************************** +# TAB 11 +# ***************************************************************************************************************** +content_11 = tab(tab11, text1='Toxic reagents should be eliminated or replaced.', + text2='Does the method involve the use of toxic reagents or solvents?') +var_11a_text = StringVar(tab11) +# Dictionary with options +var_11a_text_choices = {'No': 1.0, + 'Yes': 0.0} +var_11a_text.set('select') -dropdown7a = ttk.OptionMenu(tab7.frame, criteria[6].optionvar_a, criteria[6].optionvar_a.get(), *tab7a_choices.keys(), - command=calc_crit_7) +dropDown_11a = OptionMenu(tab11, var_11a_text, *var_11a_text_choices.keys()) +dropDown_11a.config(wraplength=250, bg='white', justify=LEFT, width=40, anchor='w') +dropDown_11a.grid(sticky='w', row=2, column=0, padx=8, pady=8) -# the ttk style for OptionMenu does not include a style for the tk dropdown menu, -# so this has to be configured separately: -dropdown7a['menu'].config(background=colors['background'], - activeborderwidth=5, - activebackground=colors['accent'], - activeforeground=colors['text'], - foreground=colors['text']) -dropdown7a.grid(column=0, row=3, padx=8, pady=8, sticky='w') +def enabler_11b(): + if var_11a_text.get() == 'select': + return 'disabled' + elif float(var_11a_text_choices[var_11a_text.get()]) == 0.0: + return 'enabled' + else: + return 'disabled' -label_tab7 = ttk.Label(tab7.frame, text='2. Select the degree of automation:', - wraplength=280) -label_tab7.grid(column=0, row=4, sticky='w') -tab7b_choices = {'Fully automated systems': 1.0, - 'Semi-automated systems': 0.50, - 'Manual systems': 0.25} +amount_var11b = StringVar(tab11) +amount_var11b.set(0.0) -dropdown7b = ttk.OptionMenu(tab7.frame, criteria[6].optionvar_b, criteria[6].optionvar_b.get(), *tab7b_choices.keys(), - command=calc_crit_7) -dropdown7b['menu'].config(background=colors['background'], - activeborderwidth=5, - activebackground=colors['accent'], - activeforeground=colors['text'], - foreground=colors['text']) +def change_dropdown_11a(*args): + global var_11 + var_11 = 1.0 + if var_11a_text.get() == 'select': + var_11 = 1.0 + else: + var_11 = var_11a_text_choices[var_11a_text.get()] + Label(tab11, text='Amount of toxic reagents or solvents in g or mL:', wraplength=300, justify=LEFT).grid(sticky='w', row=3, column=0, padx=8, pady=8) + reagent_entry_11 = ttk.Entry(tab11, textvariable=amount_var11b, width=15, state=enabler_11b()) + reagent_entry_11.grid(sticky='w', row=4, column=0, padx=8, pady=8) + + reagent_entry_11.bind('<Return>', change_dropdown_11a) + ttk.Button(tab11, text='Set', command=change_dropdown_11a).grid(row=5, column=0, padx=8, pady=8) + + if var_11a_text.get() == 'select': + pass + elif float(var_11a_text_choices[var_11a_text.get()]) != 1.0: + try: + + if float(amount_var11b.get()) < 0.1: + var_11 = 0.8 + elif float(amount_var11b.get()) > 50.0: + var_11 = 0.0 + else: + var_11 = abs(-0.129 * log(float(amount_var11b.get())) + 0.5012) # absolute value to avoid negative + + except ValueError: + tkinter.messagebox.showerror(title='Value error', message='The amount has to be a float or an integer, e.g. 0.14 or 21.') + else: + pass + + chartPlotter() + print(var_11) + + +var_11a_text.trace('w', change_dropdown_11a) + +W_11 = weightChoice(10, 0, tab11, weight_11) + +# ***************************************************************************************************************** +# TAB 12 +# ***************************************************************************************************************** +content_12 = tab(tab12, text1='Operator\'s safety should be increased.', + text2='Select the threats which are not avoided:') + +varA = IntVar() +varB = IntVar() +varC = IntVar() +varD = IntVar() +varE = IntVar() +varF = IntVar() +varG = IntVar() + +ttk.Checkbutton(tab12, text='toxic to aquatic life', variable=varA).grid(row=2, sticky='w', padx=8) +ttk.Checkbutton(tab12, text='bioacumulative', variable=varB).grid(row=3, sticky='w', padx=8) +ttk.Checkbutton(tab12, text='persistent', variable=varC).grid(row=4, sticky='w', padx=8) +ttk.Checkbutton(tab12, text='highly flammable', variable=varD).grid(row=5, sticky='w', padx=8) +ttk.Checkbutton(tab12, text='highly oxidizable', variable=varE).grid(row=6, sticky='w', padx=8) +ttk.Checkbutton(tab12, text='explosive', variable=varF).grid(row=7, sticky='w', padx=8) +ttk.Checkbutton(tab12, text='corrosive', variable=varG).grid(row=8, sticky='w', padx=8) + + +def testPrint(): + # print(varA.get(), varB.get(), varC.get(), varD.get(), varE.get(), varF.get(), varG.get()) + global var_12a + var_12a = (varA.get() + varB.get() + varC.get() + varD.get() + varE.get() + varF.get() + varG.get()) + # print(var_12a) + global var_12 + if var_12a == 0: + var_12 = 1.0 + elif var_12a == 1: + var_12 = 0.8 + elif var_12a == 2: + var_12 = 0.6 + elif var_12a == 3: + var_12 = 0.4 + elif var_12a == 4: + var_12 = 0.2 + else: + var_12 = 0.0 + print('var_12: %f' % var_12) + + chartPlotter() + + +ttk.Button(tab12, text='Set', command=testPrint).grid(row=9, column=0, padx=8, pady=8) + +W_12 = weightChoice(10, 0, tab12, weight_12) + +################################################################################################## + +# pack the tab parent and its tabs: +tab_parent.pack(expand=1, fill='both') + +# ttk.Button(leftFrame, text='Print score', command=printScore).pack(side=BOTTOM) + +# generate the default chart at the beginning: +chartPlotter() + + +############################################################################################################ + +# generate the report in .pdf: +def generateReport(): + # connect a float in range 0.0 : 1.0 to a colour in a spectrum from red to yellow to green (256 discrete colour values): + def colorMapper(value): + cmap = LinearSegmentedColormap.from_list('rg', ["red", "yellow", "green"], N=256) + mapped_color = int(value * 255) + color = cmap(mapped_color) + color_255 = [] + for band in color: + color_255.append(int(band * 255)) + return tuple(color_255) + + pdf = FPDF('P', 'mm', 'A4') + pdf.set_font('Arial', '', 10) + pdf.add_page() + pdf.set_margins(left=30, top=30) + + # save a temp image to the program's location: + plt.savefig('temp_figure.png', bbox_inches='tight') + # insert image (image, x, y, width): + pdf.image('temp_figure.png', 107, 10, 80) + # delete the temp file from drive: + os.remove('temp_figure.png') + + # insert title (Arial, 'B'old, 14 pt): + pdf.set_font('Arial', 'B', 14.0) + pdf.ln(10) + pdf.cell(100, 12, 'Analytical Greenness report sheet') + pdf.set_font('Arial', '', 12) + pdf.ln(15) + + now = datetime.now() + pdf.cell(100, 12, now.strftime("%d/%m/%Y %H:%M:%S")) + + # Text height is the same as current font size + th = pdf.font_size + 2 + + # a function to change the colour of a field based on the value: + def fieldColor(score): + x = colorMapper(score)[0] + y = colorMapper(score)[1] + z = colorMapper(score)[2] + pdf.set_fill_color(x, y, z) + + pdf.ln(70) + # populate the table + # Table head: + pdf.set_font('Arial', 'B', 10) + pdf.cell(120, th, 'Criteria', border=0) + pdf.cell(15, th, 'Score', border=0) + pdf.cell(15, th, 'Weight', border=0) + pdf.set_font('Arial', '', 10) + pdf.ln(th) + + pdf.set_fill_color(240, 240, 240) + + # Rule 1 + # Save top coordinate + top = pdf.y + # Calculate x position of next cell + offset = pdf.x + 120 + pdf.multi_cell(120, th * 0.8, '1. Direct analytical techniques should be applied to avoid sample treatment.', border=1, fill=True) + # Reset y coordinate + pdf.y = top + # Move to computed offset + pdf.x = offset + fieldColor(var_1) + pdf.cell(15, th * 1.6, str(round(var_1, 2)), border=1, fill=True, align='C') + pdf.set_fill_color(240, 240, 240) + pdf.cell(15, th * 1.6, str(weight_1.get()), border=1, fill=True, align='C') + pdf.ln(th * 2) + + # Rule 2 + top = pdf.y + offset = pdf.x + 120 + pdf.cell(120, th * 1.6, '2. Minimal sample size and minimal number of samples are goals.', border=1, fill=True) + # Reset y coordinate + pdf.y = top + # Move to computed offset + pdf.x = offset + fieldColor(var_2) + pdf.cell(15, th * 1.6, str(round(var_2, 2)), border=1, fill=True, align='C') + pdf.set_fill_color(240, 240, 240) + pdf.cell(15, th * 1.6, str(weight_2.get()), border=1, fill=True, align='C') + pdf.ln(th * 2) + + # Rule 3 + top = pdf.y + offset = pdf.x + 120 + pdf.cell(120, th * 1.6, '3. If possible, measurements should be performed in situ.', border=1, fill=True) + # Reset y coordinate + pdf.y = top + # Move to computed offset + pdf.x = offset + fieldColor(var_3) + pdf.cell(15, th * 1.6, str(round(var_3, 2)), border=1, fill=True, align='C') + pdf.set_fill_color(240, 240, 240) + pdf.cell(15, th * 1.6, str(weight_3.get()), border=1, fill=True, align='C') + pdf.ln(th * 2) + + # Rule 4 + top = pdf.y + offset = pdf.x + 120 + pdf.multi_cell(120, th * 0.8, '4. Integration of analytical processes and operations saves energy and reduces the use of reagents.', border=1, fill=True) + # Reset y coordinate + pdf.y = top + # Move to computed offset + pdf.x = offset + fieldColor(var_4) + pdf.cell(15, th * 1.6, str(round(var_4, 2)), border=1, fill=True, align='C') + pdf.set_fill_color(240, 240, 240) + pdf.cell(15, th * 1.6, str(weight_4.get()), border=1, fill=True, align='C') + pdf.ln(th * 2) + + # Rule 5 + top = pdf.y + offset = pdf.x + 120 + pdf.cell(120, th * 1.6, '5. Automated and miniaturized methods should be selected.', border=1, fill=True) + # Reset y coordinate + pdf.y = top + # Move to computed offset + pdf.x = offset + fieldColor(var_5) + pdf.cell(15, th * 1.6, str(round(var_5, 2)), border=1, fill=True, align='C') + pdf.set_fill_color(240, 240, 240) + pdf.cell(15, th * 1.6, str(weight_5.get()), border=1, fill=True, align='C') + pdf.ln(th * 2) + + # Rule 6 + top = pdf.y + offset = pdf.x + 120 + pdf.cell(120, th * 1.6, '6. Derivatization should be avoided.', border=1, fill=True) + # Reset y coordinate + pdf.y = top + # Move to computed offset + pdf.x = offset + fieldColor(var_6) + pdf.cell(15, th * 1.6, str(round(var_6, 2)), border=1, fill=True, align='C') + pdf.set_fill_color(240, 240, 240) + pdf.cell(15, th * 1.6, str(weight_6.get()), border=1, fill=True, align='C') + pdf.ln(th * 2) + + # Rule 7 + top = pdf.y + offset = pdf.x + 120 + pdf.multi_cell(120, th * 0.8, '7. Generation of a large volume of analytical waste should be avoided, and proper management of analytical waste should be provided.', border=1, fill=True) + # Reset y coordinate + pdf.y = top + # Move to computed offset + pdf.x = offset + fieldColor(var_7) + pdf.cell(15, th * 1.6, str(round(var_7, 2)), border=1, fill=True, align='C') + pdf.set_fill_color(240, 240, 240) + pdf.cell(15, th * 1.6, str(weight_7.get()), border=1, fill=True, align='C') + pdf.ln(th * 2) + + # Rule 8 + top = pdf.y + offset = pdf.x + 120 + pdf.multi_cell(120, th * 0.8, '8. Multi-analyte or multi-parameter methods are preferred versus methods using one analyte at a time.', border=1, fill=True) + # Reset y coordinate + pdf.y = top + # Move to computed offset + pdf.x = offset + fieldColor(var_8) + pdf.cell(15, th * 1.6, str(round(var_8, 2)), border=1, fill=True, align='C') + pdf.set_fill_color(240, 240, 240) + pdf.cell(15, th * 1.6, str(weight_8.get()), border=1, fill=True, align='C') + pdf.ln(th * 2) + + # Rule 9 + top = pdf.y + offset = pdf.x + 120 + pdf.cell(120, th * 1.6, '9. The use of energy should be minimized.', border=1, fill=True) + # Reset y coordinate + pdf.y = top + # Move to computed offset + pdf.x = offset + fieldColor(var_9) + pdf.cell(15, th * 1.6, str(round(var_9, 2)), border=1, fill=True, align='C') + pdf.set_fill_color(240, 240, 240) + pdf.cell(15, th * 1.6, str(weight_9.get()), border=1, fill=True, align='C') + pdf.ln(th * 2) + + # Rule 10 + top = pdf.y + offset = pdf.x + 120 + pdf.cell(120, th * 1.6, '10. Reagents obtained from renewable sources should be preferred.', border=1, fill=True) + # Reset y coordinate + pdf.y = top + # Move to computed offset + pdf.x = offset + fieldColor(var_10) + pdf.cell(15, th * 1.6, str(round(var_10, 2)), border=1, fill=True, align='C') + pdf.set_fill_color(240, 240, 240) + pdf.cell(15, th * 1.6, str(weight_10.get()), border=1, fill=True, align='C') + pdf.ln(th * 2) + + # Rule 11 + top = pdf.y + offset = pdf.x + 120 + pdf.cell(120, th * 1.6, '11. Toxic reagents should be eliminated or replaced.', border=1, fill=True) + # Reset y coordinate + pdf.y = top + # Move to computed offset + pdf.x = offset + fieldColor(var_11) + pdf.cell(15, th * 1.6, str(round(var_11, 2)), border=1, fill=True, align='C') + pdf.set_fill_color(240, 240, 240) + pdf.cell(15, th * 1.6, str(weight_11.get()), border=1, fill=True, align='C') + pdf.ln(th * 2) + + # Rule 12 + top = pdf.y + offset = pdf.x + 120 + pdf.cell(120, th * 1.6, '12. Operator\'s safety should be increased.', border=1, fill=True) + # Reset y coordinate + pdf.y = top + # Move to computed offset + pdf.x = offset + fieldColor(var_12) + pdf.cell(15, th * 1.6, str(round(var_12, 2)), border=1, fill=True, align='C') + pdf.set_fill_color(240, 240, 240) + pdf.cell(15, th * 1.6, str(weight_12.get()), border=1, fill=True, align='C') + pdf.ln(th * 2) + + # output the pdf: + def savePDF(): + ftypes = [('PDF file', '.pdf'), ('All files', '*')] + filename = filedialog.asksaveasfilename(filetypes=ftypes, defaultextension='.pdf') + # save the pdf + pdf.output(filename, 'F') + # open the file in the system default viewer: + os.system('start ' + filename) + + savePDF() -dropdown7b.grid(column=0, row=5, padx=8, pady=8, sticky='w') -# --------------------------- TAB8 --------------------------- -# remember to include a drop-down list of generic requirements for typical devices! -tab8 = Tab(tab_no='8', title='Energy consumption', - text1='The power consumption per analysis should be minimized.', - text2='Input the energy consumption [Wh] per sample, accounting for the sample throughput:', - criterion=criteria[7]) +# add the report functionality to the file menu: +FileMenu.add_command(label='Generate report', command=generateReport) +FileMenu.add_separator() -f8 = tk.Frame(tab8.frame, border=2, background=colors['accent']) -f8.grid(column=0, row=3, padx=8, pady=8, sticky='w') -entry8 = ttk.Entry(f8, textvariable=criteria[7].valuevar, width=10, style='AGREE_2.0_theme') -entry8.pack() +# add a button to refresh the chart: +refreshButton = ttk.Button(leftFrame, text='RE-GENERATE PLOT', width=20, + command=lambda: [printScore(), + clearFrame(rightFrame), + pieChart(), + print_variables() + ]) +refreshButton.pack(side=BOTTOM, anchor=SE) +########################################################################################################## -def calc_crit_8(event=None): - try: - if criteria[7].valuevar.get() == 'Input': - criteria[7].textvar.set(1.0) - elif float(criteria[7].valuevar.get()) >= 500: - criteria[7].textvar.set(0) - elif float(criteria[7].valuevar.get()) <= 10: - criteria[7].textvar.set(1.0) - else: - criteria[7].textvar.set( - abs(-0.256 * log(float(criteria[7].valuevar.get())) + 1.5886)) - except ValueError: - messagebox.showerror(title='Value error', - message='The amount has to be a float or an integer, e.g. 0.14 or 21.') - create_plot() - - -entry8.bind('<Return>', calc_crit_8) -ttk.Button(tab8.frame, text='Set', command=calc_crit_8, width=8).grid(column=0, row=3, padx=80, pady=8, sticky='w') - -# --------------------------- TAB9 --------------------------- -tab9 = Tab(tab_no='9', title='Post-sample preparation configuration for analysis', - text1='The prepared sample matrix should be compatible with the analytical instrument, ' - 'which in turn affects the overall environmental impact of the procedure.', - text2='Select the final determination technique or its closest analogue:', - criterion=criteria[8]) - -tab9_choices = {'Simple, readily available detection: smartphones, desktop scanners, paper strips, etc.': 1.0, - 'Spectrophotometry, surface analysis techniques, voltammetry, potentiometry, etc.': 0.75, - 'Gas chromatography with non-MS detection, atomic absorption spectroscopy, etc.': 0.50, - 'Liquid chromatography, gas chromatography with quadrupole detection, etc.': 0.25, - 'Adv. mass spec. with high energy and/or noble gas consumption: ICP-OES, ICP-MS, etc.': 0} - -dropdown9 = ttk.OptionMenu(tab9.frame, criteria[8].optionvar, criteria[8].optionvar.get(), *tab9_choices.keys(), - command=lambda x: change_dropdown(criteria[8], tab9_choices)) -# the ttk style for OptionMenu does not include a style for the tk dropdown menu, -# so this has to be configured separately: -dropdown9['menu'].config(background=colors['background'], - activeborderwidth=5, - activebackground=colors['accent'], - activeforeground=colors['text'], - foreground=colors['text']) - -dropdown9.grid(column=0, row=3, padx=8, pady=8, sticky='w') - -# --------------------------- TAB10 --------------------------- -tab10 = Tab(tab_no='10', title='Operator\'s safety', - text1='Hazards associated with the procedure, including physical ones, should ' - 'be minimized.', - text2='The number of distinct hazards of chemical (threats indicated in pictograms ' - 'labelling used chemicals) and physical nature:', - criterion=criteria[9]) - -tab10_choices = {'No hazards or no exposure': 1.0, - '1 hazard': 0.75, - '2 hazards': 0.50, - '3 hazards': 0.25, - '4 or more hazards': 0} -dropdown10 = ttk.OptionMenu(tab10.frame, criteria[9].optionvar, criteria[9].optionvar.get(), *tab10_choices.keys(), - command=lambda x: change_dropdown(criteria[9], tab10_choices)) -# the ttk style for OptionMenu does not include a style for the tk dropdown menu, -# so this has to be configured separately: -dropdown10['menu'].config(background=colors['background'], - activeborderwidth=5, - activebackground=colors['accent'], - activeforeground=colors['text'], - foreground=colors['text']) - -dropdown10.grid(column=0, row=3, padx=8, pady=8, sticky='w') - -''' -End of the tabs -''' - -# Create a list of the tabs and generate the report: -tabs = [tab1, tab2, tab3, tab4, tab5, tab6, tab7, tab8, tab9, tab10] - - -def main(): - while True: - root.mainloop() - - -main() +################################################################################################## +root.mainloop() # to keep the window continuously on, otherwise it shall disappear -- GitLab