From 180adbd6572a3ff45fba9b7ff87ec213d600542a Mon Sep 17 00:00:00 2001 From: JacksonBurns <33505528+JacksonBurns@users.noreply.github.com> Date: Mon, 15 Aug 2022 16:21:25 -0400 Subject: [PATCH 01/16] Update requirements.txt add customtkintr --- requirements.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/requirements.txt b/requirements.txt index 7d8a12cf..5e1eef30 100644 --- a/requirements.txt +++ b/requirements.txt @@ -14,3 +14,4 @@ rdkit-pypi psutil padelpy plotly +customtkinter From c275e3e0de9daf368f98ddee8f5b1af6f8c0ecee Mon Sep 17 00:00:00 2001 From: JacksonBurns <33505528+JacksonBurns@users.noreply.github.com> Date: Mon, 15 Aug 2022 16:23:19 -0400 Subject: [PATCH 02/16] switch rdkit to new official package --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 5e1eef30..4d44209c 100644 --- a/requirements.txt +++ b/requirements.txt @@ -10,7 +10,7 @@ mordred==1.2.0 PyYAML==5.4.1 scikit_learn==0.24.2 networkx==2.5 -rdkit-pypi +rdkit psutil padelpy plotly From 3d9ad1645bbe9975f475235b14f14ac5af5eda29 Mon Sep 17 00:00:00 2001 From: JacksonBurns <33505528+JacksonBurns@users.noreply.github.com> Date: Mon, 15 Aug 2022 16:53:54 -0400 Subject: [PATCH 03/16] MVP completed --- interfaces/UI/AIMSim_ui_main.py | 94 ++++++++++++++++++--------------- 1 file changed, 51 insertions(+), 43 deletions(-) diff --git a/interfaces/UI/AIMSim_ui_main.py b/interfaces/UI/AIMSim_ui_main.py index 0f931370..79108655 100644 --- a/interfaces/UI/AIMSim_ui_main.py +++ b/interfaces/UI/AIMSim_ui_main.py @@ -22,7 +22,10 @@ import pkg_resources -class AIMSimUiApp: +import customtkinter as ctk + + +class AIMSimUiApp(ctk.CTk): """User interface to access key functionalities of AIMSim.""" def __init__(self, master=None): @@ -32,15 +35,15 @@ def __init__(self, master=None): master (tk, optional): tk window. Defaults to None. """ # build ui - self.window = tk.Tk() if master is None else tk.Toplevel(master) + self.window = ctk.CTk() if master is None else ctk.CTkToplevel(master) self.window.title("AIMSim") # add the logo - resource_path = pkg_resources.resource_filename( - __name__, - "AIMSim-logo.png", - ) - self.window.iconphoto(False, tk.PhotoImage(file=resource_path)) + # resource_path = pkg_resources.resource_filename( + # __name__, + # "AIMSim-logo.png", + # ) + # self.window.iconphoto(False, tk.PhotoImage(file=resource_path)) # setup attributes to hold files, variables, etc. self.databaseFile = tk.StringVar(self.window) @@ -49,25 +52,28 @@ def __init__(self, master=None): self.molecularDescriptor = tk.StringVar(self.window) # title - self.titleLabel = ttk.Label(self.window) + self.titleLabel = ctk.CTkLabel(self.window) self.titleLabel.configure( font="TkDefaultFont 14 bold", text="AI Molecular Similarity") self.titleLabel.place(anchor="center", relx="0.5", rely="0.05", x="0", y="0") - self.mainframe = ttk.Frame(self.window) + self.mainframe = ctk.CTkFrame(self.window) # checkbox for verbosity - self.verboseCheckbutton = ttk.Checkbutton(self.mainframe) - self.verboseCheckbutton.configure( - compound="top", cursor="arrow", offvalue="False", onvalue="True" + self.verboseCheckbutton = ctk.CTkCheckBox( + self.mainframe, + cursor="arrow", + offvalue="False", + onvalue="True", + state=tk.NORMAL, + text="Verbose", ) - self.verboseCheckbutton.configure(state="normal", text="Verbose") self.verboseCheckbutton.place( anchor="center", relx="0.1", rely="0.95", x="0", y="0" ) # text entry field for molecule database - self.databaseFileEntry = ttk.Entry( + self.databaseFileEntry = ctk.CTkEntry( self.mainframe, textvariable=self.databaseFile ) _text_ = """smiles_responses.txt""" @@ -78,7 +84,7 @@ def __init__(self, master=None): ) # database file browser button - self.browseButton = ttk.Button(self.mainframe) + self.browseButton = ctk.CTkButton(self.mainframe) self.browseButton.configure(text="Browse...") self.browseButton.place( anchor="center", @@ -90,14 +96,14 @@ def __init__(self, master=None): self.browseButton.configure(command=self.browseCallback) # label for database entry line - self.databaseFileLabel = ttk.Label(self.mainframe) + self.databaseFileLabel = ctk.CTkLabel(self.mainframe) self.databaseFileLabel.configure(text="Database File:") self.databaseFileLabel.place( anchor="center", relx="0.5", rely="0.02", x="0", y="0" ) # entry field for target molecule - self.targetMoleculeEntry = ttk.Entry( + self.targetMoleculeEntry = ctk.CTkEntry( self.mainframe, textvariable=self.targetMolecule ) _text_ = """CO""" @@ -108,21 +114,21 @@ def __init__(self, master=None): ) # label for target molecule - self.targetMoleculeLabel = ttk.Label(self.mainframe) + self.targetMoleculeLabel = ctk.CTkLabel(self.mainframe) self.targetMoleculeLabel.configure(text="Target Molecule:") self.targetMoleculeLabel.place( anchor="center", relx="0.5", rely="0.22", x="0", y="0" ) # checkbox for database similarity plots - self.similarityPlotsCheckbutton = ttk.Checkbutton(self.mainframe) + self.similarityPlotsCheckbutton = ctk.CTkCheckBox(self.mainframe) self.similarityPlotsCheckbutton.configure(text="Similarity Plots") self.similarityPlotsCheckbutton.place( anchor="center", relx="0.3", rely="0.15", x="0", y="0" ) # checkbox for property similarity plot - self.propertySimilarityCheckbutton = ttk.Checkbutton(self.mainframe) + self.propertySimilarityCheckbutton = ctk.CTkCheckBox(self.mainframe) self.propertySimilarityCheckbutton.configure( text="Property Similarity Plot") self.propertySimilarityCheckbutton.place( @@ -130,45 +136,44 @@ def __init__(self, master=None): ) # Similarity plot for target molecule - self.similarityPlotCheckbutton = ttk.Checkbutton(self.mainframe) + self.similarityPlotCheckbutton = ctk.CTkCheckBox(self.mainframe) self.similarityPlotCheckbutton.configure(text="Similarity Plot") self.similarityPlotCheckbutton.place( anchor="center", relx="0.5", rely="0.35", x="0", y="0" ) # dropdown for descriptors - self.similarityMeasureCombobox = ttk.Combobox( - self.mainframe, textvariable=self.similarityMeasure, state="readonly" + self.similarityMeasureCombobox = ctk.CTkComboBox( + self.mainframe, variable=self.similarityMeasure, state="readonly" ) self.similarityMeasureCombobox.configure( takefocus=False, values=SimilarityMeasure.get_supported_metrics() ) - self.similarityMeasureCombobox.current(0) + self.similarityMeasureCombobox.set(0) self.similarityMeasureCombobox.place( anchor="center", relx="0.5", rely="0.46", x="0", y="0" ) # label for similarity metric - self.similarityMeasureLabel = ttk.Label(self.mainframe) + self.similarityMeasureLabel = ctk.CTkLabel(self.mainframe) self.similarityMeasureLabel.configure(text="Similarity Measure:") self.similarityMeasureLabel.place( anchor="center", relx="0.5", rely="0.4", x="0", y="0" ) # label for descriptor dropdown - self.molecularDescriptorLabel = ttk.Label(self.mainframe) + self.molecularDescriptorLabel = ctk.CTkLabel(self.mainframe) self.molecularDescriptorLabel.configure(text="Molecular Descriptor:") self.molecularDescriptorLabel.place( anchor="center", relx="0.5", rely="0.54", x="0", y="0" ) # do not allow changes - self.molecularDescriptorCombobox = ttk.Combobox( - self.mainframe, textvariable=self.molecularDescriptor, state="readonly" + self.molecularDescriptorCombobox = ctk.CTkComboBox( + self.mainframe, variable=self.molecularDescriptor, state="readonly" ) self.molecularDescriptorCombobox.configure( cursor="arrow", - justify="left", takefocus=False, values=Descriptor.get_supported_fprints(), ) @@ -191,17 +196,17 @@ def updateCompatibleMetricsListener(event): self.molecularDescriptorCombobox.place( anchor="center", relx="0.5", rely="0.60", x="0", y="0" ) - self.molecularDescriptorCombobox.current(0) + self.molecularDescriptorCombobox.set(0) # button to run AIMSim - self.runButton = ttk.Button(self.mainframe) + self.runButton = ctk.CTkButton(self.mainframe) self.runButton.configure(text="Run") self.runButton.place(anchor="center", relx="0.5", rely="0.75", x="0", y="0") self.runButton.configure(command=self.runCallback) # uses default editor to open underlying config file button - self.openConfigButton = ttk.Button(self.mainframe) + self.openConfigButton = ctk.CTkButton(self.mainframe) self.openConfigButton.configure(text="Open Config") self.openConfigButton.place( anchor="center", relx="0.5", rely="0.85", x="0", y="0" @@ -209,9 +214,8 @@ def updateCompatibleMetricsListener(event): self.openConfigButton.configure(command=self.openConfigCallback) # checkbox to show all descriptors in AIMSim - self.showAllDescriptorsButton = ttk.Checkbutton(self.mainframe) - self.showAllDescriptorsButton.configure( - compound="top", + self.showAllDescriptorsButton = ctk.CTkCheckBox( + self.mainframe, cursor="arrow", offvalue="False", onvalue="True", @@ -225,24 +229,28 @@ def updateCompatibleMetricsListener(event): ) # multiprocessing checkbox - self.multiprocessingCheckbutton = ttk.Checkbutton(self.mainframe) - self.multiprocessingCheckbutton.configure( - compound="top", cursor="arrow", offvalue="False", onvalue="True" + self.multiprocessingCheckbutton = ctk.CTkCheckBox( + self.mainframe, + cursor="arrow", + offvalue="False", + onvalue="True" ) self.multiprocessingCheckbutton.configure( - state="normal", text="Enable Multiple Workers" + state=tk.NORMAL, text="Enable Multiple Workers" ) self.multiprocessingCheckbutton.place( anchor="center", relx="0.78", rely="0.95", x="0", y="0" ) # checkbox for outlier checking - self.identifyOutliersCheckbutton = ttk.Checkbutton(self.mainframe) - self.identifyOutliersCheckbutton.configure( - compound="top", cursor="arrow", offvalue="False", onvalue="True" + self.identifyOutliersCheckbutton = ctk.CTkCheckBox( + self.mainframe, + cursor="arrow", + offvalue="False", + onvalue="True", ) self.identifyOutliersCheckbutton.configure( - state="normal", text="Outlier Check") + state=tk.NORMAL, text="Outlier Check") self.identifyOutliersCheckbutton.place( anchor="center", relx="0.4", rely="0.95", x="0", y="0" ) From 89ab077665931042bfed6466d6c30809ed709419 Mon Sep 17 00:00:00 2001 From: JacksonBurns <33505528+JacksonBurns@users.noreply.github.com> Date: Tue, 16 Aug 2022 17:19:00 -0400 Subject: [PATCH 04/16] new corner logo, gradually switching to grid --- interfaces/UI/AIMSim-GUI-corner-logo.png | Bin 0 -> 31374 bytes interfaces/UI/AIMSim_ui_main.py | 103 +++++++++++++---------- 2 files changed, 58 insertions(+), 45 deletions(-) create mode 100644 interfaces/UI/AIMSim-GUI-corner-logo.png diff --git a/interfaces/UI/AIMSim-GUI-corner-logo.png b/interfaces/UI/AIMSim-GUI-corner-logo.png new file mode 100644 index 0000000000000000000000000000000000000000..56fbd2b8cd3d97ac36436491b2df82383fb28e61 GIT binary patch literal 31374 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 literal 0 HcmV?d00001 diff --git a/interfaces/UI/AIMSim_ui_main.py b/interfaces/UI/AIMSim_ui_main.py index 79108655..ebe3e267 100644 --- a/interfaces/UI/AIMSim_ui_main.py +++ b/interfaces/UI/AIMSim_ui_main.py @@ -27,54 +27,67 @@ class AIMSimUiApp(ctk.CTk): """User interface to access key functionalities of AIMSim.""" + WIDTH = 800 + HEIGHT = 1200 - def __init__(self, master=None): - """Constructor for AIMSim. - Args: - master (tk, optional): tk window. Defaults to None. + def __init__(self): + """Constructor for AIMSim UI. """ + super().__init__() # build ui - self.window = ctk.CTk() if master is None else ctk.CTkToplevel(master) - self.window.title("AIMSim") + self.title("AIMSim") + self.geometry(f"{AIMSimUiApp.WIDTH}x{AIMSimUiApp.HEIGHT}") + self.protocol("WM_DELETE_WINDOW", self.destroy) + self.frame = ctk.CTkFrame( + master=self, + width=AIMSimUiApp.WIDTH, + ) # add the logo - # resource_path = pkg_resources.resource_filename( - # __name__, - # "AIMSim-logo.png", - # ) - # self.window.iconphoto(False, tk.PhotoImage(file=resource_path)) + resource_path = pkg_resources.resource_filename( + __name__, + "AIMSim-GUI-corner-logo.png", + ) + self.wm_iconphoto(False, tk.PhotoImage(file=resource_path)) # setup attributes to hold files, variables, etc. - self.databaseFile = tk.StringVar(self.window) - self.targetMolecule = tk.StringVar(self.window) - self.similarityMeasure = tk.StringVar(self.window) - self.molecularDescriptor = tk.StringVar(self.window) + self.databaseFile = tk.StringVar(self.frame) + self.targetMolecule = tk.StringVar(self.frame) + self.similarityMeasure = tk.StringVar(self.frame) + self.molecularDescriptor = tk.StringVar(self.frame) # title - self.titleLabel = ctk.CTkLabel(self.window) + self.titleLabel = ctk.CTkLabel(self.frame) self.titleLabel.configure( font="TkDefaultFont 14 bold", text="AI Molecular Similarity") - self.titleLabel.place(anchor="center", relx="0.5", - rely="0.05", x="0", y="0") - self.mainframe = ctk.CTkFrame(self.window) + # self.titleLabel.place(anchor="center", relx="0.5", + # rely="0.05", x="0", y="0") + self.titleLabel.grid( + row=0, + column=0, + ) # checkbox for verbosity self.verboseCheckbutton = ctk.CTkCheckBox( - self.mainframe, + self.frame, cursor="arrow", offvalue="False", onvalue="True", state=tk.NORMAL, text="Verbose", ) - self.verboseCheckbutton.place( - anchor="center", relx="0.1", rely="0.95", x="0", y="0" + # self.verboseCheckbutton.place( + # anchor="center", relx="0.1", rely="0.95", x="0", y="0" + # ) + self.verboseCheckbutton.grid( + row=1, + column=0, ) # text entry field for molecule database self.databaseFileEntry = ctk.CTkEntry( - self.mainframe, textvariable=self.databaseFile + self.frame, textvariable=self.databaseFile ) _text_ = """smiles_responses.txt""" self.databaseFileEntry.delete("0", "end") @@ -84,7 +97,7 @@ def __init__(self, master=None): ) # database file browser button - self.browseButton = ctk.CTkButton(self.mainframe) + self.browseButton = ctk.CTkButton(self.frame) self.browseButton.configure(text="Browse...") self.browseButton.place( anchor="center", @@ -96,7 +109,7 @@ def __init__(self, master=None): self.browseButton.configure(command=self.browseCallback) # label for database entry line - self.databaseFileLabel = ctk.CTkLabel(self.mainframe) + self.databaseFileLabel = ctk.CTkLabel(self.frame) self.databaseFileLabel.configure(text="Database File:") self.databaseFileLabel.place( anchor="center", relx="0.5", rely="0.02", x="0", y="0" @@ -104,7 +117,7 @@ def __init__(self, master=None): # entry field for target molecule self.targetMoleculeEntry = ctk.CTkEntry( - self.mainframe, textvariable=self.targetMolecule + self.frame, textvariable=self.targetMolecule ) _text_ = """CO""" self.targetMoleculeEntry.delete("0", "end") @@ -114,21 +127,21 @@ def __init__(self, master=None): ) # label for target molecule - self.targetMoleculeLabel = ctk.CTkLabel(self.mainframe) + self.targetMoleculeLabel = ctk.CTkLabel(self.frame) self.targetMoleculeLabel.configure(text="Target Molecule:") self.targetMoleculeLabel.place( anchor="center", relx="0.5", rely="0.22", x="0", y="0" ) # checkbox for database similarity plots - self.similarityPlotsCheckbutton = ctk.CTkCheckBox(self.mainframe) + self.similarityPlotsCheckbutton = ctk.CTkCheckBox(self.frame) self.similarityPlotsCheckbutton.configure(text="Similarity Plots") self.similarityPlotsCheckbutton.place( anchor="center", relx="0.3", rely="0.15", x="0", y="0" ) # checkbox for property similarity plot - self.propertySimilarityCheckbutton = ctk.CTkCheckBox(self.mainframe) + self.propertySimilarityCheckbutton = ctk.CTkCheckBox(self.frame) self.propertySimilarityCheckbutton.configure( text="Property Similarity Plot") self.propertySimilarityCheckbutton.place( @@ -136,7 +149,7 @@ def __init__(self, master=None): ) # Similarity plot for target molecule - self.similarityPlotCheckbutton = ctk.CTkCheckBox(self.mainframe) + self.similarityPlotCheckbutton = ctk.CTkCheckBox(self.frame) self.similarityPlotCheckbutton.configure(text="Similarity Plot") self.similarityPlotCheckbutton.place( anchor="center", relx="0.5", rely="0.35", x="0", y="0" @@ -144,7 +157,7 @@ def __init__(self, master=None): # dropdown for descriptors self.similarityMeasureCombobox = ctk.CTkComboBox( - self.mainframe, variable=self.similarityMeasure, state="readonly" + self.frame, variable=self.similarityMeasure, state="readonly" ) self.similarityMeasureCombobox.configure( takefocus=False, values=SimilarityMeasure.get_supported_metrics() @@ -155,14 +168,14 @@ def __init__(self, master=None): ) # label for similarity metric - self.similarityMeasureLabel = ctk.CTkLabel(self.mainframe) + self.similarityMeasureLabel = ctk.CTkLabel(self.frame) self.similarityMeasureLabel.configure(text="Similarity Measure:") self.similarityMeasureLabel.place( anchor="center", relx="0.5", rely="0.4", x="0", y="0" ) # label for descriptor dropdown - self.molecularDescriptorLabel = ctk.CTkLabel(self.mainframe) + self.molecularDescriptorLabel = ctk.CTkLabel(self.frame) self.molecularDescriptorLabel.configure(text="Molecular Descriptor:") self.molecularDescriptorLabel.place( anchor="center", relx="0.5", rely="0.54", x="0", y="0" @@ -170,7 +183,7 @@ def __init__(self, master=None): # do not allow changes self.molecularDescriptorCombobox = ctk.CTkComboBox( - self.mainframe, variable=self.molecularDescriptor, state="readonly" + self.frame, variable=self.molecularDescriptor, state="readonly" ) self.molecularDescriptorCombobox.configure( cursor="arrow", @@ -199,14 +212,14 @@ def updateCompatibleMetricsListener(event): self.molecularDescriptorCombobox.set(0) # button to run AIMSim - self.runButton = ctk.CTkButton(self.mainframe) + self.runButton = ctk.CTkButton(self.frame) self.runButton.configure(text="Run") self.runButton.place(anchor="center", relx="0.5", rely="0.75", x="0", y="0") self.runButton.configure(command=self.runCallback) # uses default editor to open underlying config file button - self.openConfigButton = ctk.CTkButton(self.mainframe) + self.openConfigButton = ctk.CTkButton(self.frame) self.openConfigButton.configure(text="Open Config") self.openConfigButton.place( anchor="center", relx="0.5", rely="0.85", x="0", y="0" @@ -215,7 +228,7 @@ def updateCompatibleMetricsListener(event): # checkbox to show all descriptors in AIMSim self.showAllDescriptorsButton = ctk.CTkCheckBox( - self.mainframe, + self.frame, cursor="arrow", offvalue="False", onvalue="True", @@ -230,7 +243,7 @@ def updateCompatibleMetricsListener(event): # multiprocessing checkbox self.multiprocessingCheckbutton = ctk.CTkCheckBox( - self.mainframe, + self.frame, cursor="arrow", offvalue="False", onvalue="True" @@ -244,7 +257,7 @@ def updateCompatibleMetricsListener(event): # checkbox for outlier checking self.identifyOutliersCheckbutton = ctk.CTkCheckBox( - self.mainframe, + self.frame, cursor="arrow", offvalue="False", onvalue="True", @@ -256,16 +269,16 @@ def updateCompatibleMetricsListener(event): ) # dimensions of window - self.mainframe.configure(height="400", width="400") - self.mainframe.place(anchor="nw", relheight="0.9", + self.frame.configure(height="400", width="400") + self.frame.place(anchor="nw", relheight="0.9", rely="0.1", x="0", y="0") - self.window.configure( + self.frame.configure( cursor="arrow", height="400", relief="flat", takefocus=False ) - self.window.configure(width="400") + self.frame.configure(width="400") # Main widget - self.mainwindow = self.window + self.mainwindow = self.frame def browseCallback(self): """launch a file dialog and set the databse field""" @@ -394,7 +407,7 @@ def runCallback(self): def run(self): """Start the UI.""" - self.mainwindow.mainloop() + self.mainloop() def main(): From 32fc099b327f464a2bd0d249a8bf0362bdd0ce1c Mon Sep 17 00:00:00 2001 From: JacksonBurns Date: Wed, 17 Aug 2022 22:27:23 -0400 Subject: [PATCH 05/16] fix descriptor combobox callback --- interfaces/UI/AIMSim_ui_main.py | 36 ++++++++++++++++++++------------- 1 file changed, 22 insertions(+), 14 deletions(-) diff --git a/interfaces/UI/AIMSim_ui_main.py b/interfaces/UI/AIMSim_ui_main.py index ebe3e267..9af62299 100644 --- a/interfaces/UI/AIMSim_ui_main.py +++ b/interfaces/UI/AIMSim_ui_main.py @@ -28,8 +28,7 @@ class AIMSimUiApp(ctk.CTk): """User interface to access key functionalities of AIMSim.""" WIDTH = 800 - HEIGHT = 1200 - + HEIGHT = 800 def __init__(self): """Constructor for AIMSim UI. @@ -41,7 +40,7 @@ def __init__(self): self.protocol("WM_DELETE_WINDOW", self.destroy) self.frame = ctk.CTkFrame( master=self, - width=AIMSimUiApp.WIDTH, + # width=AIMSimUiApp.WIDTH, ) # add the logo @@ -162,7 +161,9 @@ def __init__(self): self.similarityMeasureCombobox.configure( takefocus=False, values=SimilarityMeasure.get_supported_metrics() ) - self.similarityMeasureCombobox.set(0) + self.similarityMeasureCombobox.set( + self.similarityMeasureCombobox.values[0] + ) self.similarityMeasureCombobox.place( anchor="center", relx="0.5", rely="0.46", x="0", y="0" ) @@ -199,7 +200,9 @@ def updateCompatibleMetricsListener(event): ] = SimilarityMeasure.get_compatible_metrics().get( self.molecularDescriptor.get(), "Error" ) - self.similarityMeasureCombobox.current(0) + self.similarityMeasureCombobox.current( + self.similarityMeasureCombobox.values[0] + ) return # bind this listener to the combobox @@ -209,7 +212,9 @@ def updateCompatibleMetricsListener(event): self.molecularDescriptorCombobox.place( anchor="center", relx="0.5", rely="0.60", x="0", y="0" ) - self.molecularDescriptorCombobox.set(0) + self.molecularDescriptorCombobox.set( + self.molecularDescriptorCombobox.values[0] + ) # button to run AIMSim self.runButton = ctk.CTkButton(self.frame) @@ -271,7 +276,7 @@ def updateCompatibleMetricsListener(event): # dimensions of window self.frame.configure(height="400", width="400") self.frame.place(anchor="nw", relheight="0.9", - rely="0.1", x="0", y="0") + rely="0.1", x="0", y="0") self.frame.configure( cursor="arrow", height="400", relief="flat", takefocus=False ) @@ -298,14 +303,17 @@ def browseCallback(self): def showAllDescriptorsCallback(self): """update the descriptors dropdown to show descriptors.""" - if "selected" in self.showAllDescriptorsButton.state(): - self.molecularDescriptorCombobox[ - "values" - ] = Descriptor.get_all_supported_descriptors() + + if self.showAllDescriptorsButton.get(): + self.molecularDescriptorCombobox.configure( + True, + values=Descriptor.get_all_supported_descriptors(), + ) else: - self.molecularDescriptorCombobox[ - "values" - ] = values = Descriptor.get_supported_fprints() + self.molecularDescriptorCombobox.configure( + True, + values=Descriptor.get_supported_fprints(), + ) return def openConfigCallback(self): From 5ad6104cffb1fb60742a7c8bc68962b5ed7240ee Mon Sep 17 00:00:00 2001 From: JacksonBurns Date: Wed, 17 Aug 2022 23:45:21 -0400 Subject: [PATCH 06/16] callbacks fixed, api calls unified, basics done now need to customize the layout more and left justify --- interfaces/UI/AIMSim_ui_main.py | 417 +++++++++++++++++++------------- 1 file changed, 253 insertions(+), 164 deletions(-) diff --git a/interfaces/UI/AIMSim_ui_main.py b/interfaces/UI/AIMSim_ui_main.py index 9af62299..1d0d016d 100644 --- a/interfaces/UI/AIMSim_ui_main.py +++ b/interfaces/UI/AIMSim_ui_main.py @@ -24,11 +24,14 @@ import customtkinter as ctk +ctk.set_appearance_mode("system") # Modes: system (default), light, dark +ctk.set_default_color_theme("blue") # Themes: blue (default), dark-blue, green + class AIMSimUiApp(ctk.CTk): """User interface to access key functionalities of AIMSim.""" - WIDTH = 800 - HEIGHT = 800 + WIDTH = 400 + HEIGHT = 300 def __init__(self): """Constructor for AIMSim UI. @@ -36,12 +39,10 @@ def __init__(self): super().__init__() # build ui self.title("AIMSim") - self.geometry(f"{AIMSimUiApp.WIDTH}x{AIMSimUiApp.HEIGHT}") + self.minsize(AIMSimUiApp.WIDTH, AIMSimUiApp.HEIGHT) self.protocol("WM_DELETE_WINDOW", self.destroy) - self.frame = ctk.CTkFrame( - master=self, - # width=AIMSimUiApp.WIDTH, - ) + self.grid_rowconfigure((0, 1, 2, 3, 4, 5, 6, 7, 8), weight=1) + self.grid_columnconfigure((0, 1, 2), weight=1) # add the logo resource_path = pkg_resources.resource_filename( @@ -51,142 +52,193 @@ def __init__(self): self.wm_iconphoto(False, tk.PhotoImage(file=resource_path)) # setup attributes to hold files, variables, etc. - self.databaseFile = tk.StringVar(self.frame) - self.targetMolecule = tk.StringVar(self.frame) - self.similarityMeasure = tk.StringVar(self.frame) - self.molecularDescriptor = tk.StringVar(self.frame) + self.databaseFile = tk.StringVar(master=self) + self.targetMolecule = tk.StringVar(master=self) + self.similarityMeasure = tk.StringVar(master=self) + self.molecularDescriptor = tk.StringVar(master=self) + # row 0 # title - self.titleLabel = ctk.CTkLabel(self.frame) - self.titleLabel.configure( - font="TkDefaultFont 14 bold", text="AI Molecular Similarity") - # self.titleLabel.place(anchor="center", relx="0.5", - # rely="0.05", x="0", y="0") + self.titleLabel = ctk.CTkLabel( + master=self, + text_font=("Consolas", "26"), + text="AI Molecular Similarity", + ) self.titleLabel.grid( row=0, column=0, + columnspan=3, + padx=20, + pady=(5, 0), + sticky="", ) - # checkbox for verbosity - self.verboseCheckbutton = ctk.CTkCheckBox( - self.frame, - cursor="arrow", - offvalue="False", - onvalue="True", - state=tk.NORMAL, - text="Verbose", + # row 1 + # label for database entry line + self.databaseFileLabel = ctk.CTkLabel( + master=self, + text="Database File:", ) - # self.verboseCheckbutton.place( - # anchor="center", relx="0.1", rely="0.95", x="0", y="0" - # ) - self.verboseCheckbutton.grid( + self.databaseFileLabel.grid( row=1, column=0, + columnspan=1, + padx=20, + pady=(5, 0), + sticky="", ) - # text entry field for molecule database self.databaseFileEntry = ctk.CTkEntry( - self.frame, textvariable=self.databaseFile + master=self, textvariable=self.databaseFile ) _text_ = """smiles_responses.txt""" self.databaseFileEntry.delete("0", "end") self.databaseFileEntry.insert("0", _text_) - self.databaseFileEntry.place( - anchor="center", relx="0.5", rely="0.08", x="0", y="0" + self.databaseFileEntry.grid( + row=1, + column=1, + columnspan=1, + padx=20, + pady=(5, 0), + sticky="", ) - # database file browser button - self.browseButton = ctk.CTkButton(self.frame) - self.browseButton.configure(text="Browse...") - self.browseButton.place( - anchor="center", - relx="0.85", - rely="0.08", - x="0", - y="0", + self.browseButton = ctk.CTkButton( + master=self, + text="Browse...", + command=self.browseCallback, + ) + self.browseButton.grid( + row=1, + column=2, + columnspan=1, + padx=20, + pady=(5, 0), + sticky="", ) - self.browseButton.configure(command=self.browseCallback) - # label for database entry line - self.databaseFileLabel = ctk.CTkLabel(self.frame) - self.databaseFileLabel.configure(text="Database File:") - self.databaseFileLabel.place( - anchor="center", relx="0.5", rely="0.02", x="0", y="0" + # row 2 + # checkbox for database similarity plots + self.similarityPlotsCheckbutton = ctk.CTkCheckBox( + master=self, + text="Similarity Plots", + ) + self.similarityPlotsCheckbutton.grid( + row=2, + column=0, + columnspan=1, + padx=20, + pady=(5, 0), + sticky="", + ) + # checkbox for property similarity plot + self.propertySimilarityCheckbutton = ctk.CTkCheckBox( + master=self, + text="Property Similarity Plot", + ) + self.propertySimilarityCheckbutton.grid( + row=2, + column=2, + columnspan=1, + padx=20, + pady=(5, 0), + sticky="", ) + # row 3 + # label for target molecule + self.targetMoleculeLabel = ctk.CTkLabel( + master=self, + text="Target Molecule:", + ) + self.targetMoleculeLabel.grid( + row=3, + column=0, + columnspan=1, + padx=20, + pady=(5, 0), + sticky="", + ) # entry field for target molecule self.targetMoleculeEntry = ctk.CTkEntry( - self.frame, textvariable=self.targetMolecule + master=self, textvariable=self.targetMolecule ) _text_ = """CO""" self.targetMoleculeEntry.delete("0", "end") self.targetMoleculeEntry.insert("0", _text_) - self.targetMoleculeEntry.place( - anchor="center", relx="0.5", rely="0.28", x="0", y="0" - ) - - # label for target molecule - self.targetMoleculeLabel = ctk.CTkLabel(self.frame) - self.targetMoleculeLabel.configure(text="Target Molecule:") - self.targetMoleculeLabel.place( - anchor="center", relx="0.5", rely="0.22", x="0", y="0" + self.targetMoleculeEntry.grid( + row=3, + column=1, + columnspan=1, + padx=20, + pady=(5, 0), + sticky="", ) - # checkbox for database similarity plots - self.similarityPlotsCheckbutton = ctk.CTkCheckBox(self.frame) - self.similarityPlotsCheckbutton.configure(text="Similarity Plots") - self.similarityPlotsCheckbutton.place( - anchor="center", relx="0.3", rely="0.15", x="0", y="0" + # row 4 + # Similarity plot for target molecule + self.similarityPlotCheckbutton = ctk.CTkCheckBox( + master=self, + text="Similarity Plot", ) - - # checkbox for property similarity plot - self.propertySimilarityCheckbutton = ctk.CTkCheckBox(self.frame) - self.propertySimilarityCheckbutton.configure( - text="Property Similarity Plot") - self.propertySimilarityCheckbutton.place( - anchor="center", relx="0.7", rely="0.15", x="0", y="0" + self.similarityPlotCheckbutton.grid( + row=4, + column=1, + columnspan=1, + padx=20, + pady=(5, 0), + sticky="", ) - # Similarity plot for target molecule - self.similarityPlotCheckbutton = ctk.CTkCheckBox(self.frame) - self.similarityPlotCheckbutton.configure(text="Similarity Plot") - self.similarityPlotCheckbutton.place( - anchor="center", relx="0.5", rely="0.35", x="0", y="0" + # row 5 + # label for similarity metric + self.similarityMeasureLabel = ctk.CTkLabel(master=self) + self.similarityMeasureLabel.configure(text="Similarity Measure:") + self.similarityMeasureLabel.grid( + row=5, + column=0, + columnspan=1, + padx=20, + pady=(5, 0), + sticky="", ) - - # dropdown for descriptors + # dropdown for similarity measure self.similarityMeasureCombobox = ctk.CTkComboBox( - self.frame, variable=self.similarityMeasure, state="readonly" - ) - self.similarityMeasureCombobox.configure( - takefocus=False, values=SimilarityMeasure.get_supported_metrics() + master=self, + variable=self.similarityMeasure, + state="readonly", + takefocus=False, + values=SimilarityMeasure.get_supported_metrics(), ) self.similarityMeasureCombobox.set( self.similarityMeasureCombobox.values[0] ) - self.similarityMeasureCombobox.place( - anchor="center", relx="0.5", rely="0.46", x="0", y="0" - ) - - # label for similarity metric - self.similarityMeasureLabel = ctk.CTkLabel(self.frame) - self.similarityMeasureLabel.configure(text="Similarity Measure:") - self.similarityMeasureLabel.place( - anchor="center", relx="0.5", rely="0.4", x="0", y="0" + self.similarityMeasureCombobox.grid( + row=5, + column=1, + columnspan=2, + padx=20, + pady=(5, 0), + sticky="", ) + # row 6 # label for descriptor dropdown - self.molecularDescriptorLabel = ctk.CTkLabel(self.frame) + self.molecularDescriptorLabel = ctk.CTkLabel(master=self) self.molecularDescriptorLabel.configure(text="Molecular Descriptor:") - self.molecularDescriptorLabel.place( - anchor="center", relx="0.5", rely="0.54", x="0", y="0" + self.molecularDescriptorLabel.grid( + row=6, + column=0, + columnspan=1, + padx=20, + pady=(5, 0), + sticky="", ) - - # do not allow changes + # dropdown for molecular descriptor self.molecularDescriptorCombobox = ctk.CTkComboBox( - self.frame, variable=self.molecularDescriptor, state="readonly" - ) - self.molecularDescriptorCombobox.configure( + master=self, + variable=self.molecularDescriptor, + state="readonly", cursor="arrow", takefocus=False, values=Descriptor.get_supported_fprints(), @@ -195,10 +247,11 @@ def __init__(self): # define the callback for the descriptor def updateCompatibleMetricsListener(event): """Show only compatible metrics, given a descriptor.""" - self.similarityMeasureCombobox[ - "values" - ] = SimilarityMeasure.get_compatible_metrics().get( - self.molecularDescriptor.get(), "Error" + self.similarityMeasureCombobox.configure( + True, + values=SimilarityMeasure.get_compatible_metrics().get( + self.molecularDescriptor.get(), "Error" + ) ) self.similarityMeasureCombobox.current( self.similarityMeasureCombobox.values[0] @@ -209,81 +262,110 @@ def updateCompatibleMetricsListener(event): self.molecularDescriptorCombobox.bind( "<>", updateCompatibleMetricsListener ) - self.molecularDescriptorCombobox.place( - anchor="center", relx="0.5", rely="0.60", x="0", y="0" + self.molecularDescriptorCombobox.grid( + row=6, + column=1, + columnspan=1, + padx=20, + pady=(5, 0), + sticky="", ) self.molecularDescriptorCombobox.set( self.molecularDescriptorCombobox.values[0] ) - - # button to run AIMSim - self.runButton = ctk.CTkButton(self.frame) - self.runButton.configure(text="Run") - self.runButton.place(anchor="center", relx="0.5", - rely="0.75", x="0", y="0") - self.runButton.configure(command=self.runCallback) - - # uses default editor to open underlying config file button - self.openConfigButton = ctk.CTkButton(self.frame) - self.openConfigButton.configure(text="Open Config") - self.openConfigButton.place( - anchor="center", relx="0.5", rely="0.85", x="0", y="0" - ) - self.openConfigButton.configure(command=self.openConfigCallback) - # checkbox to show all descriptors in AIMSim self.showAllDescriptorsButton = ctk.CTkCheckBox( - self.frame, + master=self, cursor="arrow", - offvalue="False", - onvalue="True", command=self.showAllDescriptorsCallback, + state="normal", + text="Show experimental descriptors", ) - self.showAllDescriptorsButton.configure( - state="normal", text="Show experimental descriptors" - ) - self.showAllDescriptorsButton.place( - anchor="center", relx="0.5", rely="0.67", x="0", y="0" + self.showAllDescriptorsButton.grid( + row=6, + column=2, + columnspan=1, + padx=20, + pady=(5, 0), + sticky="", ) - # multiprocessing checkbox - self.multiprocessingCheckbutton = ctk.CTkCheckBox( - self.frame, - cursor="arrow", - offvalue="False", - onvalue="True" + # row 7 + # button to run AIMSim + self.runButton = ctk.CTkButton( + master=self, + text="Run", + command=self.runCallback, + ) + self.runButton.grid( + row=7, + column=0, + columnspan=1, + padx=20, + pady=(5, 0), + sticky="", ) - self.multiprocessingCheckbutton.configure( - state=tk.NORMAL, text="Enable Multiple Workers" + # uses default editor to open underlying config file button + self.openConfigButton = ctk.CTkButton( + master=self, + text="Open Config", + command=self.openConfigCallback, ) - self.multiprocessingCheckbutton.place( - anchor="center", relx="0.78", rely="0.95", x="0", y="0" + self.openConfigButton.grid( + row=7, + column=1, + columnspan=1, + padx=20, + pady=(5, 0), + sticky="", ) + # row 8 + # checkbox for verbosity + self.verboseCheckbutton = ctk.CTkCheckBox( + master=self, + cursor="arrow", + state=tk.NORMAL, + text="Verbose", + ) + self.verboseCheckbutton.grid( + row=8, + column=0, + columnspan=1, + padx=20, + pady=(5, 0), + sticky="", + ) # checkbox for outlier checking self.identifyOutliersCheckbutton = ctk.CTkCheckBox( - self.frame, + master=self, cursor="arrow", - offvalue="False", - onvalue="True", + state=tk.NORMAL, + text="Outlier Check", ) - self.identifyOutliersCheckbutton.configure( - state=tk.NORMAL, text="Outlier Check") - self.identifyOutliersCheckbutton.place( - anchor="center", relx="0.4", rely="0.95", x="0", y="0" + self.identifyOutliersCheckbutton.grid( + row=8, + column=1, + columnspan=1, + padx=20, + pady=(5, 0), + sticky="", ) - - # dimensions of window - self.frame.configure(height="400", width="400") - self.frame.place(anchor="nw", relheight="0.9", - rely="0.1", x="0", y="0") - self.frame.configure( - cursor="arrow", height="400", relief="flat", takefocus=False + # multiprocessing checkbox + self.multiprocessingCheckbutton = ctk.CTkCheckBox( + master=self, + cursor="arrow", + state=tk.NORMAL, + text="Enable Multiple Workers", + ) + self.multiprocessingCheckbutton.grid( + row=8, + column=2, + columnspan=1, + padx=20, + pady=(5, 0), + sticky="", ) - self.frame.configure(width="400") - - # Main widget - self.mainwindow = self.frame def browseCallback(self): """launch a file dialog and set the databse field""" @@ -314,6 +396,11 @@ def showAllDescriptorsCallback(self): True, values=Descriptor.get_supported_fprints(), ) + # switch off unsupported descriptor + if self.molecularDescriptorCombobox.current_value not in Descriptor.get_supported_fprints(): + self.molecularDescriptorCombobox.set( + self.molecularDescriptorCombobox.values[0] + ) return def openConfigCallback(self): @@ -333,7 +420,7 @@ def runCallback(self): tasks_dict = {} inner_dict = {} - if "selected" in self.similarityPlotsCheckbutton.state(): + if self.similarityPlotsCheckbutton.get(): inner_dict["similarity_plot_settings"] = { "plot_color": "green", "plot_title": "Molecule Database Similarity Distribution", @@ -345,9 +432,9 @@ def runCallback(self): } if len(inner_dict) > 0: tasks_dict["visualize_dataset"] = inner_dict - if "selected" in self.identifyOutliersCheckbutton.state(): + if self.identifyOutliersCheckbutton.get(): tasks_dict["identify_outliers"] = {"output": "terminal"} - if "selected" in self.similarityPlotCheckbutton.state(): + if self.similarityPlotCheckbutton.get(): tasks_dict["compare_target_molecule"] = { "target_molecule_smiles": self.targetMolecule.get(), "similarity_plot_settings": { @@ -356,15 +443,15 @@ def runCallback(self): }, "identify_closest_furthest": {"out_file_path": "AIMSim-ui_output.txt"}, } - if "selected" in self.propertySimilarityCheckbutton.state(): + if self.propertySimilarityCheckbutton.get(): tasks_dict["see_property_variation_w_similarity"] = { "property_file": self.databaseFile.get(), "most_dissimilar": True, "similarity_plot_settings": {"plot_color": "red"}, } - verboseChecked = "selected" in self.verboseCheckbutton.state() - if "selected" in self.multiprocessingCheckbutton.state(): + verboseChecked = self.verboseCheckbutton.get() + if self.multiprocessingCheckbutton.get(): n_workers = 'auto' else: n_workers = 1 @@ -383,7 +470,7 @@ def runCallback(self): "is_verbose": verboseChecked, "n_workers": n_workers, "molecule_database": self.databaseFile.get(), - "molecule_database_source_type": "text", + "molecule_database_source_type": molecule_database_source_type, "similarity_measure": self.similarityMeasure.get(), "fingerprint_type": self.molecularDescriptor.get(), "tasks": tasks_dict, @@ -392,8 +479,10 @@ def runCallback(self): with open("AIMSim-ui-config.yaml", "w") as outfile: yaml.dump(yamlOut, outfile, default_flow_style=False) - configs = yaml.load(open("AIMSim-ui-config.yaml", - "r"), Loader=yaml.FullLoader) + configs = yaml.load( + open("AIMSim-ui-config.yaml", "r"), + Loader=yaml.FullLoader, + ) tasks = configs.pop("tasks") if not tasks: From 459a9d5424e4191c3a0422b438b5c3458fc4aa4b Mon Sep 17 00:00:00 2001 From: JacksonBurns Date: Thu, 18 Aug 2022 15:17:52 -0400 Subject: [PATCH 07/16] more informative error for inactive bits --- aimsim/ops/similarity_measures.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/aimsim/ops/similarity_measures.py b/aimsim/ops/similarity_measures.py index 06113b8b..90a89e8c 100644 --- a/aimsim/ops/similarity_measures.py +++ b/aimsim/ops/similarity_measures.py @@ -253,8 +253,10 @@ def __call__(self, mol1_descriptor, mol2_descriptor): Returns: similarity_ (float): Similarity value """ - if not self._validate_fprint(mol1_descriptor) or not self._validate_fprint(mol2_descriptor): - raise ValueError('Molecule descriptor has no active bits') + if not self._validate_fprint(mol1_descriptor) or not not self._validate_fprint(mol2_descriptor): + raise ValueError( + f'Molecule descriptor ({mol1_descriptor.label_}) has no active bits' + ) similarity_ = None if self.metric == "l0_similarity": try: From 138266b8c4b3a2d557358e9e261f5d9edea2673f Mon Sep 17 00:00:00 2001 From: JacksonBurns Date: Thu, 18 Aug 2022 15:33:08 -0400 Subject: [PATCH 08/16] even MORE informative error --- .../chemical_datastructures/molecule_set.py | 22 ++++++++++++++----- 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/aimsim/chemical_datastructures/molecule_set.py b/aimsim/chemical_datastructures/molecule_set.py index 2e12f5bc..8ac227bf 100644 --- a/aimsim/chemical_datastructures/molecule_set.py +++ b/aimsim/chemical_datastructures/molecule_set.py @@ -84,6 +84,7 @@ class MoleculeSet: Spectral Embedding ('spectral_embedding') """ + def __init__( self, molecule_database_src, @@ -396,6 +397,10 @@ def worker(thread_idx, n_mols, start_idx, end_idx, queue): # pragma: no cover except NotInitializedError as e: e.message += "Similarity matrix could not be set " raise e + except ValueError as e: + raise RuntimeError( + f'Unable to proccess molecule {molecule.mol_text}' + ) from e queue.put(local_similarity_matrix) return None @@ -448,12 +453,17 @@ def worker(thread_idx, n_mols, start_idx, end_idx, queue): # pragma: no cover "Computing similarity of molecule num " f"{target_mol_id + 1} against {source_mol_id + 1}" ) - similarity_matrix[ - source_mol_id, target_mol_id - ] = molecule.get_similarity_to( - self.molecule_database[target_mol_id], - similarity_measure=self.similarity_measure, - ) + try: + similarity_matrix[ + source_mol_id, target_mol_id + ] = molecule.get_similarity_to( + self.molecule_database[target_mol_id], + similarity_measure=self.similarity_measure, + ) + except ValueError as e: + raise RuntimeError( + f'Unable to proccess molecule {molecule.mol_text}' + ) from e self.similarity_matrix = similarity_matrix From ff9053ba9419167fdd762be56408272c771c912a Mon Sep 17 00:00:00 2001 From: JacksonBurns Date: Thu, 18 Aug 2022 15:44:55 -0400 Subject: [PATCH 09/16] total visual redesign --- interfaces/UI/AIMSim_ui_main.py | 206 ++++++++++++++++++-------------- 1 file changed, 119 insertions(+), 87 deletions(-) diff --git a/interfaces/UI/AIMSim_ui_main.py b/interfaces/UI/AIMSim_ui_main.py index 1d0d016d..ea5c737a 100644 --- a/interfaces/UI/AIMSim_ui_main.py +++ b/interfaces/UI/AIMSim_ui_main.py @@ -23,15 +23,16 @@ import customtkinter as ctk +from idlelib.tooltip import ToolTip -ctk.set_appearance_mode("system") # Modes: system (default), light, dark +ctk.set_appearance_mode("dark") # Modes: system (default), light, dark ctk.set_default_color_theme("blue") # Themes: blue (default), dark-blue, green class AIMSimUiApp(ctk.CTk): """User interface to access key functionalities of AIMSim.""" - WIDTH = 400 - HEIGHT = 300 + WIDTH = 600 + HEIGHT = 150 def __init__(self): """Constructor for AIMSim UI. @@ -42,7 +43,7 @@ def __init__(self): self.minsize(AIMSimUiApp.WIDTH, AIMSimUiApp.HEIGHT) self.protocol("WM_DELETE_WINDOW", self.destroy) self.grid_rowconfigure((0, 1, 2, 3, 4, 5, 6, 7, 8), weight=1) - self.grid_columnconfigure((0, 1, 2), weight=1) + self.grid_columnconfigure((0, 1, 2, 3, 4), weight=1) # add the logo resource_path = pkg_resources.resource_filename( @@ -67,9 +68,9 @@ def __init__(self): self.titleLabel.grid( row=0, column=0, - columnspan=3, - padx=20, - pady=(5, 0), + columnspan=5, + padx=0, + pady=(0, 0), sticky="", ) @@ -83,9 +84,9 @@ def __init__(self): row=1, column=0, columnspan=1, - padx=20, - pady=(5, 0), - sticky="", + padx=0, + pady=(0, 0), + sticky="e", ) # text entry field for molecule database self.databaseFileEntry = ctk.CTkEntry( @@ -97,10 +98,10 @@ def __init__(self): self.databaseFileEntry.grid( row=1, column=1, - columnspan=1, - padx=20, - pady=(5, 0), - sticky="", + columnspan=3, + padx=0, + pady=(0, 0), + sticky="we", ) # database file browser button self.browseButton = ctk.CTkButton( @@ -110,10 +111,10 @@ def __init__(self): ) self.browseButton.grid( row=1, - column=2, + column=4, columnspan=1, padx=20, - pady=(5, 0), + pady=(0, 0), sticky="", ) @@ -121,14 +122,14 @@ def __init__(self): # checkbox for database similarity plots self.similarityPlotsCheckbutton = ctk.CTkCheckBox( master=self, - text="Similarity Plots", + text="Database Similarity Plot", ) self.similarityPlotsCheckbutton.grid( row=2, column=0, - columnspan=1, - padx=20, - pady=(5, 0), + columnspan=2, + padx=0, + pady=(0, 0), sticky="", ) # checkbox for property similarity plot @@ -139,9 +140,9 @@ def __init__(self): self.propertySimilarityCheckbutton.grid( row=2, column=2, - columnspan=1, - padx=20, - pady=(5, 0), + columnspan=3, + padx=0, + pady=(0, 0), sticky="", ) @@ -155,90 +156,104 @@ def __init__(self): row=3, column=0, columnspan=1, - padx=20, - pady=(5, 0), + padx=0, + pady=(0, 0), sticky="", ) # entry field for target molecule self.targetMoleculeEntry = ctk.CTkEntry( master=self, textvariable=self.targetMolecule ) - _text_ = """CO""" + _text_ = """optional""" self.targetMoleculeEntry.delete("0", "end") self.targetMoleculeEntry.insert("0", _text_) self.targetMoleculeEntry.grid( row=3, column=1, - columnspan=1, - padx=20, - pady=(5, 0), - sticky="", + columnspan=3, + padx=0, + pady=(0, 0), + sticky="we", ) - - # row 4 - # Similarity plot for target molecule - self.similarityPlotCheckbutton = ctk.CTkCheckBox( + # target molecule file browser button + self.browseTargetButton = ctk.CTkButton( master=self, - text="Similarity Plot", + text="Browse...", + command=self.browseCallback, ) - self.similarityPlotCheckbutton.grid( - row=4, - column=1, + self.browseTargetButton.grid( + row=3, + column=4, columnspan=1, padx=20, - pady=(5, 0), + pady=(0, 0), sticky="", ) - # row 5 + # row 4 # label for similarity metric self.similarityMeasureLabel = ctk.CTkLabel(master=self) self.similarityMeasureLabel.configure(text="Similarity Measure:") self.similarityMeasureLabel.grid( - row=5, + row=4, column=0, columnspan=1, - padx=20, - pady=(5, 0), + padx=0, + pady=(0, 0), sticky="", ) # dropdown for similarity measure - self.similarityMeasureCombobox = ctk.CTkComboBox( + self.similarityMeasureCombobox = ctk.CTkOptionMenu( master=self, variable=self.similarityMeasure, - state="readonly", takefocus=False, values=SimilarityMeasure.get_supported_metrics(), + hover=False, ) self.similarityMeasureCombobox.set( self.similarityMeasureCombobox.values[0] ) self.similarityMeasureCombobox.grid( - row=5, + row=4, column=1, - columnspan=2, + columnspan=3, padx=20, - pady=(5, 0), + pady=(0, 0), + sticky="we", + ) + # checkbox to automatically determine the similarity measure + self.useMeasureSearchCheckbox = ctk.CTkCheckBox( + master=self, + cursor="arrow", + command=self.useMeasureSearchCallback, + state="normal", + text="AI Search", + ) + self.useMeasureSearchCheckbox.grid( + row=4, + column=4, + columnspan=1, + padx=0, + pady=(0, 0), sticky="", ) - # row 6 + # row 5 # label for descriptor dropdown self.molecularDescriptorLabel = ctk.CTkLabel(master=self) self.molecularDescriptorLabel.configure(text="Molecular Descriptor:") self.molecularDescriptorLabel.grid( - row=6, + row=5, column=0, columnspan=1, - padx=20, - pady=(5, 0), + padx=0, + pady=(0, 0), sticky="", ) # dropdown for molecular descriptor - self.molecularDescriptorCombobox = ctk.CTkComboBox( + self.molecularDescriptorCombobox = ctk.CTkOptionMenu( master=self, variable=self.molecularDescriptor, - state="readonly", cursor="arrow", takefocus=False, values=Descriptor.get_supported_fprints(), @@ -263,12 +278,12 @@ def updateCompatibleMetricsListener(event): "<>", updateCompatibleMetricsListener ) self.molecularDescriptorCombobox.grid( - row=6, + row=5, column=1, - columnspan=1, + columnspan=3, padx=20, - pady=(5, 0), - sticky="", + pady=(0, 0), + sticky="we", ) self.molecularDescriptorCombobox.set( self.molecularDescriptorCombobox.values[0] @@ -279,18 +294,18 @@ def updateCompatibleMetricsListener(event): cursor="arrow", command=self.showAllDescriptorsCallback, state="normal", - text="Show experimental descriptors", + text="Exp. Descriptors", ) self.showAllDescriptorsButton.grid( - row=6, - column=2, + row=5, + column=4, columnspan=1, - padx=20, - pady=(5, 0), + padx=0, + pady=(0, 0), sticky="", ) - # row 7 + # row 6 # button to run AIMSim self.runButton = ctk.CTkButton( master=self, @@ -298,11 +313,11 @@ def updateCompatibleMetricsListener(event): command=self.runCallback, ) self.runButton.grid( - row=7, - column=0, + row=6, + column=1, columnspan=1, padx=20, - pady=(5, 0), + pady=(0, 0), sticky="", ) # uses default editor to open underlying config file button @@ -312,15 +327,15 @@ def updateCompatibleMetricsListener(event): command=self.openConfigCallback, ) self.openConfigButton.grid( - row=7, - column=1, + row=6, + column=3, columnspan=1, padx=20, - pady=(5, 0), + pady=(0, 0), sticky="", ) - # row 8 + # row 7 # checkbox for verbosity self.verboseCheckbutton = ctk.CTkCheckBox( master=self, @@ -329,11 +344,11 @@ def updateCompatibleMetricsListener(event): text="Verbose", ) self.verboseCheckbutton.grid( - row=8, + row=7, column=0, columnspan=1, - padx=20, - pady=(5, 0), + padx=0, + pady=(0, 0), sticky="", ) # checkbox for outlier checking @@ -344,11 +359,11 @@ def updateCompatibleMetricsListener(event): text="Outlier Check", ) self.identifyOutliersCheckbutton.grid( - row=8, + row=7, column=1, columnspan=1, - padx=20, - pady=(5, 0), + padx=0, + pady=(0, 0), sticky="", ) # multiprocessing checkbox @@ -356,17 +371,21 @@ def updateCompatibleMetricsListener(event): master=self, cursor="arrow", state=tk.NORMAL, - text="Enable Multiple Workers", + text="Multiple Processes", ) self.multiprocessingCheckbutton.grid( - row=8, + row=7, column=2, - columnspan=1, - padx=20, - pady=(5, 0), - sticky="", + columnspan=2, + padx=0, + pady=(0, 0), + sticky="w", ) + # add tooltips + ToolTip(self.openConfigButton, + "Opens a config file corresponding\nto the last run") + def browseCallback(self): """launch a file dialog and set the databse field""" out = filedialog.askopenfilename( @@ -403,6 +422,18 @@ def showAllDescriptorsCallback(self): ) return + def useMeasureSearchCallback(self): + """measure search dropdown disable/enable""" + if self.useMeasureSearchCheckbox.get(): + self.similarityMeasureCombobox.configure( + state='disabled' + ) + else: + self.similarityMeasureCombobox.configure( + state='normal' + ) + return + def openConfigCallback(self): """ Open the config file being used by the UI to allow the user to edit it. @@ -434,9 +465,10 @@ def runCallback(self): tasks_dict["visualize_dataset"] = inner_dict if self.identifyOutliersCheckbutton.get(): tasks_dict["identify_outliers"] = {"output": "terminal"} - if self.similarityPlotCheckbutton.get(): + if self.targetMolecule.get() not in ("", "optional"): tasks_dict["compare_target_molecule"] = { - "target_molecule_smiles": self.targetMolecule.get(), + "target_molecule_smiles": self.targetMolecule.get() if not os.path.exists(self.targetMolecule.get()) else None, + "target_molecule_src": self.targetMolecule.get() if os.path.exists(self.targetMolecule.get()) else None, "similarity_plot_settings": { "plot_color": "orange", "plot_title": "Molecule Database Compared to Target Molecule", @@ -457,7 +489,7 @@ def runCallback(self): n_workers = 1 _, file_extension = os.path.splitext(self.databaseFile.get()) - if file_extension == ".txt": + if file_extension.lower() in (".txt", ".smi", ".smiles"): molecule_database_source_type = "text" elif file_extension == "": molecule_database_source_type = "folder" @@ -471,7 +503,7 @@ def runCallback(self): "n_workers": n_workers, "molecule_database": self.databaseFile.get(), "molecule_database_source_type": molecule_database_source_type, - "similarity_measure": self.similarityMeasure.get(), + "similarity_measure": 'determine' if self.useMeasureSearchCheckbox.get() else self.similarityMeasure.get(), "fingerprint_type": self.molecularDescriptor.get(), "tasks": tasks_dict, } From f0c2696bd4ca1fde3252cfcaca102f8f3076d509 Mon Sep 17 00:00:00 2001 From: JacksonBurns Date: Thu, 18 Aug 2022 16:24:28 -0400 Subject: [PATCH 10/16] fix geometry issues, add tooltips --- interfaces/UI/AIMSim_ui_main.py | 47 ++++++++++++++++++++++++++++++--- 1 file changed, 43 insertions(+), 4 deletions(-) diff --git a/interfaces/UI/AIMSim_ui_main.py b/interfaces/UI/AIMSim_ui_main.py index ea5c737a..3bc08735 100644 --- a/interfaces/UI/AIMSim_ui_main.py +++ b/interfaces/UI/AIMSim_ui_main.py @@ -32,7 +32,7 @@ class AIMSimUiApp(ctk.CTk): """User interface to access key functionalities of AIMSim.""" WIDTH = 600 - HEIGHT = 150 + HEIGHT = 400 def __init__(self): """Constructor for AIMSim UI. @@ -41,6 +41,7 @@ def __init__(self): # build ui self.title("AIMSim") self.minsize(AIMSimUiApp.WIDTH, AIMSimUiApp.HEIGHT) + self.geometry(f"{AIMSimUiApp.WIDTH}x{AIMSimUiApp.HEIGHT}") self.protocol("WM_DELETE_WINDOW", self.destroy) self.grid_rowconfigure((0, 1, 2, 3, 4, 5, 6, 7, 8), weight=1) self.grid_columnconfigure((0, 1, 2, 3, 4), weight=1) @@ -383,8 +384,46 @@ def updateCompatibleMetricsListener(event): ) # add tooltips - ToolTip(self.openConfigButton, - "Opens a config file corresponding\nto the last run") + ToolTip( + self.openConfigButton, + "Open the config file\nfor the last run", + ) + ToolTip( + self.runButton, + "Write a config file\nand call AIMSim" + ) + ToolTip( + self.targetMoleculeEntry, + "SMILES string or Filepath for an 'external'\nmolecule for comparison to the others" + ) + ToolTip( + self.browseButton, + "Open a File Explorer to locate molecules\nin a supported data format" + ) + ToolTip( + self.useMeasureSearchCheckbox, + "Automatically determines best metric\nfor molecules with responses" + ) + ToolTip( + self.showAllDescriptorsButton, + "Show experimental descriptors from\nother libraries in the dropdown", + ) + ToolTip( + self.verboseCheckbutton, + "Check this for additional output\non the terminal or debugging", + ) + ToolTip( + self.identifyOutliersCheckbutton, + "Isolation Forest to identify outliers\nin sets of molecules with responses", + ) + ToolTip( + self.multiprocessingCheckbutton, + "Allow use of multiple processing\ncores (automatically configured)", + ) + ToolTip( + self.molecularDescriptorCombobox, + "Tip: Use AIMSim from the command line\nto access descriptors from Mordred and Padel" + ) def browseCallback(self): """launch a file dialog and set the databse field""" @@ -408,7 +447,7 @@ def showAllDescriptorsCallback(self): if self.showAllDescriptorsButton.get(): self.molecularDescriptorCombobox.configure( True, - values=Descriptor.get_all_supported_descriptors(), + values=Descriptor.get_all_supported_descriptors()[:15], ) else: self.molecularDescriptorCombobox.configure( From 5f8994fcd84d0c4e872c90edfbfb7d8bf33ac874 Mon Sep 17 00:00:00 2001 From: JacksonBurns Date: Thu, 18 Aug 2022 16:31:45 -0400 Subject: [PATCH 11/16] remove redundant metrics --- interfaces/UI/AIMSim_ui_main.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/interfaces/UI/AIMSim_ui_main.py b/interfaces/UI/AIMSim_ui_main.py index 3bc08735..c114a7a9 100644 --- a/interfaces/UI/AIMSim_ui_main.py +++ b/interfaces/UI/AIMSim_ui_main.py @@ -208,7 +208,7 @@ def __init__(self): master=self, variable=self.similarityMeasure, takefocus=False, - values=SimilarityMeasure.get_supported_metrics(), + values=SimilarityMeasure.get_uniq_metrics(), hover=False, ) self.similarityMeasureCombobox.set( @@ -265,9 +265,11 @@ def updateCompatibleMetricsListener(event): """Show only compatible metrics, given a descriptor.""" self.similarityMeasureCombobox.configure( True, - values=SimilarityMeasure.get_compatible_metrics().get( - self.molecularDescriptor.get(), "Error" - ) + values=[ + metric for metric in SimilarityMeasure.get_compatible_metrics().get( + self.molecularDescriptor.get(), "Error" + ) if (metric in SimilarityMeasure.get_uniq_metrics()) + ] ) self.similarityMeasureCombobox.current( self.similarityMeasureCombobox.values[0] From 80ccabaa13c52e5be90f8312714978b99c65627e Mon Sep 17 00:00:00 2001 From: JacksonBurns Date: Thu, 18 Aug 2022 16:36:47 -0400 Subject: [PATCH 12/16] remove fail fast on CI --- .github/workflows/run_tests.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/run_tests.yml b/.github/workflows/run_tests.yml index 383bc909..7cdbb93d 100644 --- a/.github/workflows/run_tests.yml +++ b/.github/workflows/run_tests.yml @@ -10,7 +10,8 @@ on: jobs: build: runs-on: ubuntu-latest - strategy: + strategy: + fail-fast: false matrix: python-version: ['3.7', '3.8', '3.9'] name: Python ${{ matrix.python-version }} sample From e4b4426fbc1d28584b245bc9776689cc69ca54ee Mon Sep 17 00:00:00 2001 From: JacksonBurns Date: Thu, 18 Aug 2022 16:39:49 -0400 Subject: [PATCH 13/16] remove 'helpful' damn error --- .../chemical_datastructures/molecule_set.py | 21 ++++++------------- 1 file changed, 6 insertions(+), 15 deletions(-) diff --git a/aimsim/chemical_datastructures/molecule_set.py b/aimsim/chemical_datastructures/molecule_set.py index 8ac227bf..8999226d 100644 --- a/aimsim/chemical_datastructures/molecule_set.py +++ b/aimsim/chemical_datastructures/molecule_set.py @@ -397,10 +397,6 @@ def worker(thread_idx, n_mols, start_idx, end_idx, queue): # pragma: no cover except NotInitializedError as e: e.message += "Similarity matrix could not be set " raise e - except ValueError as e: - raise RuntimeError( - f'Unable to proccess molecule {molecule.mol_text}' - ) from e queue.put(local_similarity_matrix) return None @@ -453,17 +449,12 @@ def worker(thread_idx, n_mols, start_idx, end_idx, queue): # pragma: no cover "Computing similarity of molecule num " f"{target_mol_id + 1} against {source_mol_id + 1}" ) - try: - similarity_matrix[ - source_mol_id, target_mol_id - ] = molecule.get_similarity_to( - self.molecule_database[target_mol_id], - similarity_measure=self.similarity_measure, - ) - except ValueError as e: - raise RuntimeError( - f'Unable to proccess molecule {molecule.mol_text}' - ) from e + similarity_matrix[ + source_mol_id, target_mol_id + ] = molecule.get_similarity_to( + self.molecule_database[target_mol_id], + similarity_measure=self.similarity_measure, + ) self.similarity_matrix = similarity_matrix From 07cbda5ed5c65e84d8dfa77be35c8aec04c55783 Mon Sep 17 00:00:00 2001 From: JacksonBurns Date: Thu, 18 Aug 2022 16:42:51 -0400 Subject: [PATCH 14/16] add back helpful error and fix except in measure_search --- .../chemical_datastructures/molecule_set.py | 21 +++++++++++++------ aimsim/tasks/measure_search.py | 2 +- 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/aimsim/chemical_datastructures/molecule_set.py b/aimsim/chemical_datastructures/molecule_set.py index 8999226d..8ac227bf 100644 --- a/aimsim/chemical_datastructures/molecule_set.py +++ b/aimsim/chemical_datastructures/molecule_set.py @@ -397,6 +397,10 @@ def worker(thread_idx, n_mols, start_idx, end_idx, queue): # pragma: no cover except NotInitializedError as e: e.message += "Similarity matrix could not be set " raise e + except ValueError as e: + raise RuntimeError( + f'Unable to proccess molecule {molecule.mol_text}' + ) from e queue.put(local_similarity_matrix) return None @@ -449,12 +453,17 @@ def worker(thread_idx, n_mols, start_idx, end_idx, queue): # pragma: no cover "Computing similarity of molecule num " f"{target_mol_id + 1} against {source_mol_id + 1}" ) - similarity_matrix[ - source_mol_id, target_mol_id - ] = molecule.get_similarity_to( - self.molecule_database[target_mol_id], - similarity_measure=self.similarity_measure, - ) + try: + similarity_matrix[ + source_mol_id, target_mol_id + ] = molecule.get_similarity_to( + self.molecule_database[target_mol_id], + similarity_measure=self.similarity_measure, + ) + except ValueError as e: + raise RuntimeError( + f'Unable to proccess molecule {molecule.mol_text}' + ) from e self.similarity_matrix = similarity_matrix diff --git a/aimsim/tasks/measure_search.py b/aimsim/tasks/measure_search.py index 20414039..8462e502 100644 --- a/aimsim/tasks/measure_search.py +++ b/aimsim/tasks/measure_search.py @@ -168,7 +168,7 @@ def __call__(self, n_threads=molecule_set_configs.get( 'n_threads', 1), sampling_ratio=subsample_subset_size) - except (InvalidConfigurationError, ValueError) as e: + except (InvalidConfigurationError, ValueError, RuntimeError) as e: if is_verbose: print(f'Could not try {fingerprint_type} with ' f'similarity measure {similarity_measure} due to ' From a5c4ddc6da40b2c1c9ceecb503959f0564ea611d Mon Sep 17 00:00:00 2001 From: JacksonBurns Date: Thu, 18 Aug 2022 16:55:18 -0400 Subject: [PATCH 15/16] why on EARTH am i allowed to type "NOT NOT" --- aimsim/ops/similarity_measures.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/aimsim/ops/similarity_measures.py b/aimsim/ops/similarity_measures.py index 90a89e8c..12f88d88 100644 --- a/aimsim/ops/similarity_measures.py +++ b/aimsim/ops/similarity_measures.py @@ -253,9 +253,9 @@ def __call__(self, mol1_descriptor, mol2_descriptor): Returns: similarity_ (float): Similarity value """ - if not self._validate_fprint(mol1_descriptor) or not not self._validate_fprint(mol2_descriptor): + if not self._validate_fprint(mol1_descriptor) or not self._validate_fprint(mol2_descriptor): raise ValueError( - f'Molecule descriptor ({mol1_descriptor.label_}) has no active bits' + f'Molecule descriptor ({mol1_descriptor.label_}) has no active bits.' ) similarity_ = None if self.metric == "l0_similarity": From 289c93606abbd1ff77a3b1223458fd8510f70430 Mon Sep 17 00:00:00 2001 From: JacksonBurns Date: Thu, 18 Aug 2022 16:59:49 -0400 Subject: [PATCH 16/16] bump version so i don't forget later --- aimsim/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aimsim/__init__.py b/aimsim/__init__.py index a08465bb..9c928b8c 100644 --- a/aimsim/__init__.py +++ b/aimsim/__init__.py @@ -3,4 +3,4 @@ from . import chemical_datastructures from . import utils -__version__ = "1.0.3" +__version__ = "1.1.0"