diff --git a/main.py b/main.py index 118dcf5c3639c78b4b7fbcd6ab0827529fda0c24..6e62803358c27fff5fe456462422039d923ba6da 100644 --- a/main.py +++ b/main.py @@ -1,3 +1,7 @@ +''' +W.Wojnowski 2021 +''' +# imports: import tkinter as tk from tkinter import ttk from tkinter import messagebox @@ -6,26 +10,27 @@ from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg from matplotlib.colors import LinearSegmentedColormap from tkinter import filedialog 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.title('AGREEPrep - *** Alpha for testing ***') +root.title('AGREEPrep - Analytical Greenness Metric for Sample Preparation') root.geometry('760x450') -# root.geometry('850x525') +root.iconbitmap('AGREEPrep_icon.ico') +# fix the window dimensions: root.resizable(0, 0) - -# light: +# define the color palette. This can be based on https://flatuicolors.com colors = { 'foreground': '#ffffff', 'background': '#dfe4ea', 'accent': '#70a1ff', 'text': 'black' -} + } -# configure the background -# color palette: https://flatuicolors.com/palette/defo +# configure the background: root.configure(bg=colors['background'], padx=8, pady=8) # create and configure a style to be used in tabs and labels @@ -141,7 +146,9 @@ def call_info_popup(): 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', wraplength=280, justify='left') + 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) @@ -181,6 +188,11 @@ def temp_func(): pass +def generate_report(): + report = Report(tabs, criteria) + report.savePDF() + + def create_menu(): menu = tk.Menu() root.config(menu=menu) @@ -194,7 +206,7 @@ def create_menu(): 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=temp_func) + file_menu.add_command(label='Report', command=generate_report) about_menu = tk.Menu(menu) about_menu.config(background=colors['background'], activeborderwidth=5, @@ -286,7 +298,7 @@ class Criterion: self.optionvar_b.set('Select') -# create the 12 criteria values. They can be later called as +# 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): @@ -323,9 +335,11 @@ def clear_frame(frame): 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') + print('clear frame done') -# 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): +# 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 colorMapper(value): cmap = LinearSegmentedColormap.from_list('rg', ["red", "yellow", "green"], N=256) mapped_color = int(value * 255) @@ -343,7 +357,7 @@ def change_dropdown(criterion, tab_choices, *args): def create_plot(event=None): # the event=None is passed so that the entry.bind - # does not return a positional argument + # does not return a positional argument; # close the current plot window (otherwise the generated plots remain open in the memory): plt.close() @@ -365,18 +379,22 @@ def create_plot(event=None): 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) + 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=0 at the top - ax.set_theta_zero_location("N") # theta increasing clockwise + 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): @@ -401,12 +419,121 @@ def create_plot(event=None): plot_widget = canvas.get_tk_widget() plot_widget.pack(side='top') - # print('final score: %s' % calculate_score()) + 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) + print(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 waste: ' + self.criteria[1].valuevar.get())) + + create_report_field('3.', self.tabs[2], self.criteria[2], + ('Mass [g] or volume [mL] of problematic materials: ' + self.criteria[2].valuevar.get())) + + create_report_field('4.', self.tabs[3], self.criteria[3], + (self.criteria[3].optionvar.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], + ('Approximate energy consumption per analysis [W]: ' + self.criteria[5].valuevar.get())) + + create_report_field('7.', self.tabs[6], self.criteria[6], + ('Hourly sample throughput: ' + self.criteria[6].valuevar.get())) + + create_report_field('8.', self.tabs[7], self.criteria[7], + ('No. of sample prep. steps: ' + self.criteria[7].optionvar_a.get() + + '; degree if automation: ' + self.criteria[7].optionvar_b.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) + + ''' Populate the tabs ''' @@ -434,7 +561,7 @@ dropdown1.grid(column=0, row=3, padx=8, pady=8, sticky='w') # # --------------------------- TAB2 --------------------------- tab2 = Tab(tab_no='2', title='Waste', - text1='In analytical chemistry, all material inputs, including' + 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[1]) # create a frame to pack the entry into in order to get a border for the entry: @@ -661,11 +788,11 @@ tab8 = Tab(tab_no='8', title='Automation', text2='1. Select the number of discrete steps in the sample preparation procedure:', criterion=criteria[7]) -tab8a_choices = {'⤠2 steps': 1.0, +tab8a_choices = {'2 steps or fewer': 1.0, '3 steps': 0.75, '4 steps': 0.50, '5 steps': 0.25, - '⼠6 steps': 0} + '6 steps or more': 0} def calc_crit_8(event=None): @@ -726,11 +853,11 @@ tab9 = Tab(tab_no='9', title='Analytical instrumentation for final determination text2='Select the final determination technique or its closest analogue:', criterion=criteria[8]) -tab9_choices = {'Simple, readily available detection: smartphones, desktop scanners, paper strips detection, etc.': 1.0, +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, - 'Advanced mass spectrometry techniques with high energy and/or noble gas consumption: ICP-OES, ICP-MS, etc.': 0} + '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)) @@ -769,12 +896,18 @@ dropdown10['menu'].config(background=colors['background'], 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(): - root.mainloop() + while True: + root.mainloop() main() - -input('Press any key to close the console')