From 0fd709a041b6c112d85963619b5711e13415f971 Mon Sep 17 00:00:00 2001 From: Brad Date: Mon, 8 Apr 2024 10:43:22 -0400 Subject: [PATCH] Can edit which files are under Systems --- MacroQueue/Dialogs.py | 67 ++-- MacroQueue/Functions/General.py | 5 +- MacroQueue/Functions/Testing.py | 24 ++ MacroQueue/GUIDesign.py | 89 ----- MacroQueue/MacroQueue.py | 54 +-- MacroQueue/Macros/CreaTecMacro.json | 61 ++++ MacroQueue/Macros/RHKMacro.json | 9 + MacroQueue/Macros/TestingMacro.json | 45 +++ docs_src/Paper/paper.md | 8 +- docs_src/source/conf.py | 2 +- setup.py | 4 +- wxGUIDesign.fbp | 497 ++-------------------------- 12 files changed, 258 insertions(+), 607 deletions(-) create mode 100644 MacroQueue/Functions/Testing.py create mode 100644 MacroQueue/Macros/RHKMacro.json create mode 100644 MacroQueue/Macros/TestingMacro.json diff --git a/MacroQueue/Dialogs.py b/MacroQueue/Dialogs.py index 16d7c2e..52e5105 100644 --- a/MacroQueue/Dialogs.py +++ b/MacroQueue/Dialogs.py @@ -11,12 +11,10 @@ from MacroQueue.GUIDesign import MacroDialog from MacroQueue.GUIDesign import MacroSettingsDialog from MacroQueue.GUIDesign import StartMacroDialog - from MacroQueue.GUIDesign import ChooseSoftware except ModuleNotFoundError: from GUIDesign import MacroDialog from GUIDesign import MacroSettingsDialog from GUIDesign import StartMacroDialog - from GUIDesign import ChooseSoftware import pandas as pd class SettingsDialog(wx.Dialog): @@ -1153,28 +1151,55 @@ def OnCancel(self, event): self.Destroy() -class MyChooseSoftwareDialog(ChooseSoftware): +class MyChooseSoftwareDialog(wx.Dialog): def __init__(self, parent,FunctionsToLoad=None): - super().__init__(parent) + wx.Dialog.__init__ ( self, parent, id = wx.ID_ANY, title = u"Choose the STM Software", pos = wx.DefaultPosition, size = wx.DefaultSize, style = wx.DEFAULT_DIALOG_STYLE ) + + self.SetSizeHints( wx.DefaultSize, wx.DefaultSize ) + self.SetBackgroundColour( wx.SystemSettings.GetColour( wx.SYS_COLOUR_INACTIVECAPTION ) ) + + fgSizer12 = wx.FlexGridSizer( 0, 1, 0, 0 ) + fgSizer12.SetFlexibleDirection( wx.BOTH ) + fgSizer12.SetNonFlexibleGrowMode( wx.FLEX_GROWMODE_SPECIFIED ) + + self.m_ChooseSoftwareText = wx.StaticText( self, wx.ID_ANY, u"Please select the STM Software you wish to use", wx.DefaultPosition, wx.DefaultSize, 0 ) + self.m_ChooseSoftwareText.Wrap( -1 ) + + fgSizer12.Add( self.m_ChooseSoftwareText, 0, wx.ALIGN_CENTER_HORIZONTAL|wx.ALL, 5 ) + + self.m_panel11 = wx.Panel( self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.TAB_TRAVERSAL ) + self.m_panel11.SetBackgroundColour( wx.SystemSettings.GetColour( wx.SYS_COLOUR_ACTIVECAPTION ) ) + + bSizer6 = wx.BoxSizer( wx.HORIZONTAL ) + for i,system in enumerate(parent.Systems): + self.m_button = wx.Button( self.m_panel11, wx.ID_ANY, system, wx.DefaultPosition, wx.DefaultSize, 0 ) + bSizer6.Add( self.m_button, 0, wx.ALL, 5 ) + SetTHISSoftware = partial(self.SetSoftware,i) + self.m_button.Bind( wx.EVT_BUTTON, SetTHISSoftware) + + + + self.m_panel11.SetSizer( bSizer6 ) + self.m_panel11.Layout() + bSizer6.Fit( self.m_panel11 ) + fgSizer12.Add( self.m_panel11, 1, wx.EXPAND |wx.ALL, 5 ) + + + self.SetSizer( fgSizer12 ) + self.Layout() + fgSizer12.Fit( self ) + + self.Centre( wx.BOTH ) + + self.SavedSettingsFile = self.Parent.SavedSettingsFile self.FunctionsToLoad = FunctionsToLoad - def OnRHK(self, event=None): - self.SetSoftware("RHK") - def OnCreaTec(self, event=None): - self.SetSoftware("CreaTec") - def OnSXM(self, event=None): - self.SetSoftware("SXM") - def SetSoftware(self,software=None): - self.Parent.m_RHKmenuItem.Check(False) - self.Parent.m_CreaTecmenuItem.Check(False) - self.Parent.m_SXMmenuItem.Check(False) - if software is not None: - if software == "RHK": - self.Parent.m_RHKmenuItem.Check(True) - if software == "CreaTec": - self.Parent.m_CreaTecmenuItem.Check(True) - if software == "SXM": - self.Parent.m_SXMmenuItem.Check(True) + def SetSoftware(self,softwareIndex=None,event=None): + for MenuItem in self.Parent.SystemMenuItems: + MenuItem.Check(False) + if softwareIndex is not None: + self.Parent.SystemMenuItems[softwareIndex].Check(True) + software = self.Parent.Systems[softwareIndex] self.Parent.Software = software self.Parent.MacroPath = self.Parent.MacroPaths[software] if self.FunctionsToLoad is None: diff --git a/MacroQueue/Functions/General.py b/MacroQueue/Functions/General.py index 5d8e07e..8199fa2 100644 --- a/MacroQueue/Functions/General.py +++ b/MacroQueue/Functions/General.py @@ -1,5 +1,8 @@ from time import sleep +Systems =['RHK','CreaTec','SXM',"Testing"] + + Cancel = False MacroQueueSelf = None @@ -11,7 +14,7 @@ # {"Name":"SomeString","Tooltip":"A String parameter produces a textbox"} # {"Name":"SomeFilePath","Tooltip":"A filepath parameter produces a 'browse' button"} # {"Name":"SomeChoice","Tooltip":"A Choice parameter produces a dropdown menu"} -def Complex_Function(SomeBoolean=True,SomeString="String",SomeFilePath="C:\\",SomeChoice=['Choice','Combo','3rd','4th']): +def Complex_Function2(SomeBoolean=True,SomeString="String",SomeFilePath="C:\\",SomeChoice=['Choice','Combo','3rd','4th']): if SomeBoolean: print(SomeString, SomeChoice) diff --git a/MacroQueue/Functions/Testing.py b/MacroQueue/Functions/Testing.py new file mode 100644 index 0000000..fdbb40e --- /dev/null +++ b/MacroQueue/Functions/Testing.py @@ -0,0 +1,24 @@ + +from time import sleep + +CurrentMacro = None +OutgoingQueue = None +Cancel = False +MacroQueueSelf = None + +def Initialize(): + pass + + +# {"Name":"WaitTime","Units":"s","Tooltip":"The time to wait"} +def Wait(WaitTime=1): + while WaitTime > 1 and not Cancel: + WaitTime-=1 + sleep(1) + if not Cancel: + sleep(WaitTime) + + +# Index=This has no impact. It's solely used to repeat the functions. +def Null(Index=0): + pass \ No newline at end of file diff --git a/MacroQueue/GUIDesign.py b/MacroQueue/GUIDesign.py index 9ea2b65..c6eda04 100644 --- a/MacroQueue/GUIDesign.py +++ b/MacroQueue/GUIDesign.py @@ -58,15 +58,6 @@ def __init__( self, parent ): self.m_menubar1.Append( self.m_Connectmenu, u"Connect" ) self.m_SystemMenu = wx.Menu() - self.m_RHKmenuItem = wx.MenuItem( self.m_SystemMenu, wx.ID_ANY, u"RHK", wx.EmptyString, wx.ITEM_CHECK ) - self.m_SystemMenu.Append( self.m_RHKmenuItem ) - - self.m_CreaTecmenuItem = wx.MenuItem( self.m_SystemMenu, wx.ID_ANY, u"CreaTec", wx.EmptyString, wx.ITEM_CHECK ) - self.m_SystemMenu.Append( self.m_CreaTecmenuItem ) - - self.m_SXMmenuItem = wx.MenuItem( self.m_SystemMenu, wx.ID_ANY, u"SXM", wx.EmptyString, wx.ITEM_CHECK ) - self.m_SystemMenu.Append( self.m_SXMmenuItem ) - self.m_menubar1.Append( self.m_SystemMenu, u"System" ) self.m_NotSTMMenu = wx.Menu() @@ -157,9 +148,6 @@ def __init__( self, parent ): self.Bind( wx.EVT_MENU, self.OpenMacroFile, id = self.m_OpenMacroMenuItem.GetId() ) self.Bind( wx.EVT_MENU, self.AddConnectToQueue, id = self.m_menuItem7.GetId() ) self.Bind( wx.EVT_MENU, self.AddDisconnectToQueue, id = self.m_menuItem8.GetId() ) - self.Bind( wx.EVT_MENU, self.OnRHKSoftware, id = self.m_RHKmenuItem.GetId() ) - self.Bind( wx.EVT_MENU, self.OnCreaTecSoftware, id = self.m_CreaTecmenuItem.GetId() ) - self.Bind( wx.EVT_MENU, self.OnSXMSoftware, id = self.m_SXMmenuItem.GetId() ) self.Bind( wx.EVT_MENU, self.BasicUseageHelp, id = self.m_menuItem10.GetId() ) self.Bind( wx.EVT_MENU, self.ExpandNumericalParameters, id = self.m_menuItem102.GetId() ) self.Bind( wx.EVT_MENU, self.MakeAMacroHelp, id = self.m_menuItem11.GetId() ) @@ -202,15 +190,6 @@ def AddConnectToQueue( self, event ): def AddDisconnectToQueue( self, event ): event.Skip() - def OnRHKSoftware( self, event ): - event.Skip() - - def OnCreaTecSoftware( self, event ): - event.Skip() - - def OnSXMSoftware( self, event ): - event.Skip() - def BasicUseageHelp( self, event ): event.Skip() @@ -239,74 +218,6 @@ def ClearQueue( self, event ): event.Skip() -########################################################################### -## Class ChooseSoftware -########################################################################### - -class ChooseSoftware ( wx.Dialog ): - - def __init__( self, parent ): - wx.Dialog.__init__ ( self, parent, id = wx.ID_ANY, title = u"Choose the STM Software", pos = wx.DefaultPosition, size = wx.DefaultSize, style = wx.DEFAULT_DIALOG_STYLE ) - - self.SetSizeHints( wx.DefaultSize, wx.DefaultSize ) - self.SetBackgroundColour( wx.SystemSettings.GetColour( wx.SYS_COLOUR_INACTIVECAPTION ) ) - - fgSizer12 = wx.FlexGridSizer( 0, 1, 0, 0 ) - fgSizer12.SetFlexibleDirection( wx.BOTH ) - fgSizer12.SetNonFlexibleGrowMode( wx.FLEX_GROWMODE_SPECIFIED ) - - self.m_ChooseSoftwareText = wx.StaticText( self, wx.ID_ANY, u"Please select the STM Software you wish to use", wx.DefaultPosition, wx.DefaultSize, 0 ) - self.m_ChooseSoftwareText.Wrap( -1 ) - - fgSizer12.Add( self.m_ChooseSoftwareText, 0, wx.ALIGN_CENTER_HORIZONTAL|wx.ALL, 5 ) - - self.m_panel11 = wx.Panel( self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.TAB_TRAVERSAL ) - self.m_panel11.SetBackgroundColour( wx.SystemSettings.GetColour( wx.SYS_COLOUR_ACTIVECAPTION ) ) - - bSizer6 = wx.BoxSizer( wx.HORIZONTAL ) - - self.m_RHKbutton = wx.Button( self.m_panel11, wx.ID_ANY, u"RHK", wx.DefaultPosition, wx.DefaultSize, 0 ) - bSizer6.Add( self.m_RHKbutton, 0, wx.ALL, 5 ) - - self.m_CreaTecbutton = wx.Button( self.m_panel11, wx.ID_ANY, u"CreaTec", wx.DefaultPosition, wx.DefaultSize, 0 ) - bSizer6.Add( self.m_CreaTecbutton, 0, wx.ALL, 5 ) - - self.m_SXMbutton = wx.Button( self.m_panel11, wx.ID_ANY, u"SXM", wx.DefaultPosition, wx.DefaultSize, 0 ) - bSizer6.Add( self.m_SXMbutton, 0, wx.ALL, 5 ) - - - self.m_panel11.SetSizer( bSizer6 ) - self.m_panel11.Layout() - bSizer6.Fit( self.m_panel11 ) - fgSizer12.Add( self.m_panel11, 1, wx.EXPAND |wx.ALL, 5 ) - - - self.SetSizer( fgSizer12 ) - self.Layout() - fgSizer12.Fit( self ) - - self.Centre( wx.BOTH ) - - # Connect Events - self.m_RHKbutton.Bind( wx.EVT_BUTTON, self.OnRHK ) - self.m_CreaTecbutton.Bind( wx.EVT_BUTTON, self.OnCreaTec ) - self.m_SXMbutton.Bind( wx.EVT_BUTTON, self.OnSXM ) - - def __del__( self ): - pass - - - # Virtual event handlers, override them in your derived class - def OnRHK( self, event ): - event.Skip() - - def OnCreaTec( self, event ): - event.Skip() - - def OnSXM( self, event ): - event.Skip() - - ########################################################################### ## Class MacroDialog ########################################################################### diff --git a/MacroQueue/MacroQueue.py b/MacroQueue/MacroQueue.py index 7ad8fdf..adc8e38 100644 --- a/MacroQueue/MacroQueue.py +++ b/MacroQueue/MacroQueue.py @@ -36,16 +36,12 @@ # from GUIDesign import MacroDialog from inspect import getmembers, isfunction -# They all go in try/except just in case the required packages aren't installed for one of them -# (e.g. you can still use RHK's functions even without win32com which you need for CreaTec) -if getattr(sys, 'frozen', False): - sys.path.append(os.path.dirname(sys.executable)+"\\Functions") -else: - sys.path.append(os.path.dirname(__file__)+"\\Functions") +sys.path.append(os.path.dirname(sys.executable if getattr(sys, 'frozen', False) else __file__)+"\\Functions") import json from PyInstaller.__main__ import run as PyInstall +from functools import partial IconFileName = "MacroQueueIcon.ico" @@ -57,13 +53,20 @@ # TODO: # Show number of items in queue in status bar -VersionNumber = "v0.2.5" +VersionNumber = "v0.3.0" # VersionNumber also in conf.py & setup.py Date = "4/2024" class MacroQueue(MyFrame): - MacroPaths = {"RHK":"Macros//RHKMacro.json","CreaTec":"Macros//CreaTecMacro.json","SXM":"Macros//SXMMacro.json"} + try: + import General + Systems = General.Systems + except: + Systems =['RHK','CreaTec','SXM',"Testing"] + NotAuxFiles = [f"{system}.py" for system in Systems] + NotAuxFiles.append("SXMRemote.py") + MacroPaths = {system:f"Macros//{system}Macro.json" for system in Systems} # Scanning, fine motion, course motion, dI/dV scans, point spectra, tip form, TheQueue = [] @@ -75,6 +78,7 @@ class MacroQueue(MyFrame): Closing = False Editting = False Functions = {} + SystemMenuItems = [] # my_module = importlib.import_module('os.path') def __init__(self,test=False): self.test = test @@ -94,6 +98,12 @@ def __init__(self,test=False): self.m_FileMenu.Append( self.m_ExitMenuItem ) self.Bind( wx.EVT_MENU, self.OnClose, id = self.m_ExitMenuItem.GetId() ) + for i,system in enumerate(self.Systems): + self.m_SystemmenuItem = wx.MenuItem( self.m_SystemMenu, wx.ID_ANY, system, wx.EmptyString, wx.ITEM_CHECK ) + self.m_SystemMenu.Append( self.m_SystemmenuItem ) + self.SystemMenuItems.append(self.m_SystemmenuItem) + OnTHISSoftware = partial(self.OnSoftware,i) + self.Bind( wx.EVT_MENU, OnTHISSoftware, id = self.m_SystemmenuItem.GetId() ) icon_file = os.path.join(os.path.abspath(os.path.dirname(__file__)), IconFileName) if os.path.exists(icon_file): icon = wx.Icon(icon_file) @@ -124,7 +134,7 @@ def __init__(self,test=False): self.m_PauseAfterCancel.Check(self.SettingsDict['PauseAfterCancel'] != 'False') self.Software = self.SettingsDict["Software"] ThisChooseSoftwareDialog = MyChooseSoftwareDialog(self,self.FunctionsLoaded) - ThisChooseSoftwareDialog.SetSoftware(self.Software) + ThisChooseSoftwareDialog.SetSoftware(self.Systems.index(self.Software)) for item in self.m_NotSTMMenu.GetMenuItems(): if item.GetItemLabel() in self.FunctionsLoaded: @@ -259,7 +269,7 @@ def LoadFunctions(self,Reloading=False): # self.Functions[f"{FunctionName[:-3]}"] = import_source_file(os.path.abspath(f'Functions\\{FunctionName}'),os.path.abspath(f'Functions\\{FunctionName}')) except Exception as e: pass - for file in ["CreaTec.py","RHK.py","SXM.py","SXMRemote.py"]: + for file in self.NotAuxFiles: try: FunctionNames.remove(file) except: @@ -273,20 +283,22 @@ def LoadFunctions(self,Reloading=False): if not Reloading: CheckFunction = self.m_NotSTMMenu.AppendCheckItem(wx.ID_ANY,file[:-3]) self.Bind(wx.EVT_MENU,self.EditLoadedFunctionFiles,CheckFunction) + CheckFunction.Check() else: MenuItems = self.m_NotSTMMenu.GetMenuItems() MenuLabels = [MenuItem.GetItemLabel() for MenuItem in MenuItems ] if not file[:-3] in MenuLabels: CheckFunction = self.m_NotSTMMenu.AppendCheckItem(wx.ID_ANY,file[:-3]) self.Bind(wx.EVT_MENU,self.EditLoadedFunctionFiles,CheckFunction) + self.EditLoadedFunctionFiles() def EditLoadedFunctionFiles(self,event=None): self.FunctionsLoaded = [] for item in self.m_NotSTMMenu.GetMenuItems(): if item.IsChecked(): self.FunctionsLoaded.append(item.GetItemLabel()) - - ThisChooseSoftwareDialog = MyChooseSoftwareDialog(self,self.FunctionsLoaded) - ThisChooseSoftwareDialog.SetSoftware(self.Software) + if self.Software is not None: + ThisChooseSoftwareDialog = MyChooseSoftwareDialog(self,self.FunctionsLoaded) + ThisChooseSoftwareDialog.SetSoftware(self.Systems.index(self.Software)) def MakeFunctionButtons(self): @@ -772,21 +784,13 @@ def AddDisconnectToQueue(self, event=None): ThisStartMacroDialog.AddToQueue() self.AddConnectToQueue() return - def OnRHKSoftware(self, event): - ThisChooseSoftwareDialog = MyChooseSoftwareDialog(self) - ThisChooseSoftwareDialog.OnRHK() - return - def OnCreaTecSoftware(self, event): - ThisChooseSoftwareDialog = MyChooseSoftwareDialog(self) - ThisChooseSoftwareDialog.OnCreaTec() - return - def OnSXMSoftware(self, event): + def OnSoftware(self,i,event): ThisChooseSoftwareDialog = MyChooseSoftwareDialog(self) - ThisChooseSoftwareDialog.OnSXM() - return + ThisChooseSoftwareDialog.SetSoftware(i) + def PauseAfterCancel(self,event): ThisChooseSoftwareDialog = MyChooseSoftwareDialog(self,self.FunctionsLoaded) - ThisChooseSoftwareDialog.SetSoftware(self.Software) + ThisChooseSoftwareDialog.SetSoftware(self.Systems.index(self.Software)) pass def ReloadFunctions(self,event): self.LoadFunctions(Reloading=True) diff --git a/MacroQueue/Macros/CreaTecMacro.json b/MacroQueue/Macros/CreaTecMacro.json index 31f78f8..afa77a0 100644 --- a/MacroQueue/Macros/CreaTecMacro.json +++ b/MacroQueue/Macros/CreaTecMacro.json @@ -1054,5 +1054,66 @@ }, false ] + ], + "Example2": [ + [ + "Wait", + { + "WaitTime": { + "DefaultValue": "1", + "Tooltip": "The time to wait", + "Frozen": false, + "ValueType": "Numerical", + "InRange": true, + "Name": "WaitTime", + "Units": "s" + } + }, + true + ], + [ + "Complex Function2", + { + "SomeBoolean": { + "DefaultValue": true, + "Tooltip": "A Boolean parameter produces a checkbox", + "Frozen": false, + "ValueType": "Boolean", + "InRange": true, + "Name": "SomeBoolean" + }, + "SomeString": { + "DefaultValue": "String", + "Tooltip": "A String parameter produces a textbox", + "Frozen": false, + "ValueType": "String", + "InRange": true, + "Name": "SomeString" + }, + "SomeFilePath": { + "DefaultValue": "C:\\", + "Tooltip": "A filepath parameter produces a 'browse' button", + "Frozen": false, + "ValueType": "File", + "InRange": true, + "Name": "SomeFilePath" + }, + "SomeChoice": { + "DefaultValue": "Choice", + "Tooltip": "A Choice parameter produces a dropdown menu", + "Frozen": false, + "ValueType": "Choice", + "InRange": true, + "DefaultList": [ + "Choice", + "Combo", + "3rd", + "4th" + ], + "Name": "SomeChoice" + } + }, + true + ] ] } \ No newline at end of file diff --git a/MacroQueue/Macros/RHKMacro.json b/MacroQueue/Macros/RHKMacro.json new file mode 100644 index 0000000..bc9f4b1 --- /dev/null +++ b/MacroQueue/Macros/RHKMacro.json @@ -0,0 +1,9 @@ +{ + "Scan": [ + [ + "Scan", + {}, + true + ] + ] +} \ No newline at end of file diff --git a/MacroQueue/Macros/TestingMacro.json b/MacroQueue/Macros/TestingMacro.json new file mode 100644 index 0000000..b72911c --- /dev/null +++ b/MacroQueue/Macros/TestingMacro.json @@ -0,0 +1,45 @@ +{ + "TestMacro": [ + [ + "Null", + { + "Index": { + "DefaultValue": "0,1", + "Tooltip": "This has no impact. It's solely used to repeat the functions.", + "Frozen": false, + "ValueType": "Numerical", + "InRange": true + } + }, + true + ], + [ + "Wait", + { + "WaitTime": { + "DefaultValue": "1", + "Tooltip": "The time to wait", + "Frozen": false, + "ValueType": "Numerical", + "InRange": true, + "Name": "WaitTime", + "Units": "s" + } + }, + true + ], + [ + "Print", + { + "Number": { + "DefaultValue": "0,5,1", + "Tooltip": "", + "Frozen": false, + "ValueType": "Numerical", + "InRange": true + } + }, + true + ] + ] +} \ No newline at end of file diff --git a/docs_src/Paper/paper.md b/docs_src/Paper/paper.md index 1bf1fd3..dd6d05a 100644 --- a/docs_src/Paper/paper.md +++ b/docs_src/Paper/paper.md @@ -32,9 +32,7 @@ These features allow users to easily control several instruments in sync, add ne # Statement of need -Numerous instruments have to be controlled in sync to access the full parameter space of an SPM system. This is typically automated with python scripts for longer measurements, such as QPI mapping which is a series of a dozen measurements that each take over an hour, see @MacroQueueDocs. Writing a python script for each type of measurement, at best, leads to the spagetti code that physists are notoriously known for, and at worst, is prohibitively difficult for novice SPM operators. MacroQueue provides a simple GUI to allow users to perform measurements without coding and allows advanced users to easily add or modify functions to control new and existing equipment. - -This software is currently in active use in several laboratories at The Ohio State University and the NSF NeXUS Facility. It assisted in the research of @Goff_2024 and @koll2024formation. Several similar packages and APIs already exist, such as @PyMeasure, @bluesky, and @ScopeFoundry2023. A good overview of available Python packages can be found in @buchner_2022_6399528. The goal of MacroQueue is to provide a frontend GUI that allows users to perform measurements in high-dimensional parameter spaces without requiring the coding ability that is necessary to use the existing APIs while still providing advanced users the flexibility to write arbitrarily complex functions. +Numerous instruments have to be controlled in sync to access the full parameter space of an SPM system. This is typically automated with python scripts for longer measurements. MacroQueue provides a simple GUI to allow users to perform measurements without coding and allows advanced users to easily add or modify functions to control new and existing equipment. This software is currently in active use in several laboratories containing STMs at The Ohio State University and the NSF NeXUS Facility. It assisted in the research of @Goff_2024 and @koll2024formation. Several similar packages and APIs already exist, such as @PyMeasure, @bluesky, and @ScopeFoundry2023. A good overview of available Python packages can be found in @buchner_2022_6399528. The goal of MacroQueue is to provide a frontend GUI that allows users to perform measurements in high-dimensional parameter spaces without requiring the coding ability that is necessary to use the existing APIs while still providing advanced users the flexibility to write arbitrarily complex functions. # Overview @@ -42,11 +40,11 @@ This software is currently in active use in several laboratories at The Ohio Sta -MacroQueue can be packaged into an executable, by either using the provided script or using PyInstaller directly, so that coding and using the command line is completely optional. To further allow the user experience to be as simple as possible, any arbitrary python function, no matter how basic, can be add to MacroQueue. Everything else is handled automatically. Even as a executable, users can open the "source folder" via the File menu to access the python files that control various intstruments. Upon launching, MacroQueue searches this folder for new files. Every function, from each python file, is dynamically imported using the package importlib, part of python's standard library. For each parameter in a function, MacroQueue reads the default value to interpret the datatype (e.g. string, numerical, boolean, list, filepath) and the appriate control in the GUI (e.g. text box, numbers only text box, checkmark, dropdown menu, file browser respectively). \autoref{fig:ExampleCode} shows example code and the various controls that are produced in the GUI. +MacroQueue can be packaged into an executable, by either using the provided script or using PyInstaller directly, so that coding and using the command line is completely optional. To further allow the user experience to be as simple as possible, any arbitrary python function, no matter how basic, can be added to MacroQueue. Even as a executable, users can open the "source folder" via the File menu to access the python files that control various intstruments. Upon launching, MacroQueue searches this folder for new files. Every function, from each python file, is dynamically imported using the package importlib, part of python's standard library. For each parameter in a function, MacroQueue reads the default value to interpret the datatype (e.g. string, numerical, boolean, list, filepath) and the appriate control in the GUI (e.g. text box, numbers only text box, checkmark, dropdown menu, file browser respectively). \autoref{fig:ExampleCode} shows example code and the various controls that are produced in the GUI. ![The workflow for adding a new function and defining a new macro.\label{fig:ExampleCode}](Figure1.png) -For additional features, users can write metadata for each parameter in comments above each function. The metadata includes units and an explation of what the parameter does, which will be included in the parameters's hover tooltip. Numerical values can also have a soft minimum and/or maximum value in their metadata; when a users tries to input a value outside the range, there will be a pop-up warning to confirm that they want to proced. Hard limits can be applied in indiviual funtions by throwing an exception in typical pythonic fashion. If an exception is thrown, intentional or otherwise, in any of the functions, the queue will be paused, the current macro will be canceled, and a pop-up will provide the user with the exception's details. +For additional features, users can write metadata for each parameter in comments above each function. The metadata includes units and an explation of what the parameter does, which will be included in the parameters's hover tooltip. Numerical values can also have a soft minimum and/or maximum value in their metadata; when a users tries to input a value outside the range, there will be a pop-up warning to confirm that they want to proced. Hard limits can be applied in individual function by raising an exception, such as a ValueError exception. If an exception is thrown in any of the functions, intentional or otherwise, the queue will be paused, the current macro will be canceled, and a pop-up will provide the user with the exception's details. ![The workflow for adding macros to the queue.\label{fig:AddMacro}](Figure2.png) diff --git a/docs_src/source/conf.py b/docs_src/source/conf.py index 7ab611d..8cc0d37 100644 --- a/docs_src/source/conf.py +++ b/docs_src/source/conf.py @@ -9,7 +9,7 @@ project = 'MacroQueue' copyright = '2024, Brad Goff' author = 'Brad Goff' -release = '0.2.5' +release = '0.3.0' # -- General configuration --------------------------------------------------- # https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration diff --git a/setup.py b/setup.py index 17b609f..fba7cd1 100644 --- a/setup.py +++ b/setup.py @@ -13,13 +13,13 @@ setup( name = 'MacroQueue', # How you named your package folder (MyLib) packages = ['MacroQueue'], # Chose the same as "name" - version = '0.2.5', # Start with a small number and increase it with every change you make + version = '0.3.0', # Start with a small number and increase it with every change you make license='MIT', # Chose a license from here: https://help.github.com/articles/licensing-a-repository description = 'Automating Scanning Probe Microscopy', # Give a short description about your library author = 'Brad Goff', # Type in your name author_email = 'guptagroupstm@gmail.com', # Type in your E-Mail url = 'https://github.com/guptagroupstm/STMMacroQueue', # Provide either the link to your github or to your website - download_url = 'https://github.com/guptagroupstm/STMMacroQueue/archive/refs/tags/v0.2.5.tar.gz', # I explain this later on + download_url = 'https://github.com/guptagroupstm/STMMacroQueue/archive/refs/tags/v0.3.0.tar.gz', # I explain this later on keywords = ['Automation', 'Scanning Probe Microscopy', 'Macro',"Queue"], # Keywords that define your package best install_requires=[ # I get to this in a second 'pandas', diff --git a/wxGUIDesign.fbp b/wxGUIDesign.fbp index 4925ad6..605754f 100644 --- a/wxGUIDesign.fbp +++ b/wxGUIDesign.fbp @@ -29,7 +29,7 @@ 0 0 0 - + 0 wxAUI_MGR_DEFAULT wxSYS_COLOUR_APPWORKSPACE @@ -60,7 +60,7 @@ OnClose IdleLoop OnSize - + 1 @@ -81,7 +81,7 @@ wxTRANSPARENT_WINDOW - + File m_FileMenu protected @@ -100,11 +100,11 @@ OpenSourceFolder - + Options m_OptionsMenu protected - + 1 1 @@ -117,7 +117,7 @@ - + 0 1 @@ -132,7 +132,7 @@ ReloadFunctions - + Macro m_MacroMenu protected @@ -165,7 +165,7 @@ OpenMacroFile - + Connect m_Connectmenu protected @@ -198,59 +198,17 @@ AddDisconnectToQueue - + System m_SystemMenu protected - - - 0 - 1 - - wxID_ANY - wxITEM_CHECK - RHK - m_RHKmenuItem - none - - - OnRHKSoftware - - - - 0 - 1 - - wxID_ANY - wxITEM_CHECK - CreaTec - m_CreaTecmenuItem - none - - - OnCreaTecSoftware - - - - 0 - 1 - - wxID_ANY - wxITEM_CHECK - SXM - m_SXMmenuItem - none - - - OnSXMSoftware - - + Included Functions m_NotSTMMenu protected - + Help m_menu5 protected @@ -324,7 +282,7 @@ InfoHelp - + 0 1 @@ -372,7 +330,7 @@ protected CheckQueue - + 3 wxBOTH 0 @@ -502,11 +460,11 @@ wxVSCROLL - + 5 wxEXPAND | wxALL 1 - + 1 1 1 @@ -557,7 +515,7 @@ wxTAB_TRAVERSAL - + 1 wxBOTH 0 @@ -723,393 +681,6 @@ - 0 - wxAUI_MGR_DEFAULT - wxSYS_COLOUR_INACTIVECAPTION - wxBOTH - - 1 - 1 - impl_virtual - - - - 0 - wxID_ANY - - - ChooseSoftware - - - wxDEFAULT_DIALOG_STYLE - ; ; forward_declare - Choose the STM Software - - 0 - - - - - 1 - wxBOTH - - - 0 - - fgSizer12 - wxFLEX_GROWMODE_SPECIFIED - none - 0 - 0 - - 5 - wxALIGN_CENTER_HORIZONTAL|wxALL - 0 - - 1 - 1 - 1 - 1 - - - - - - - - 1 - 0 - 1 - - 1 - 0 - Dock - 0 - Left - 1 - - 1 - - 0 - 0 - wxID_ANY - Please select the STM Software you wish to use - 0 - - 0 - - - 0 - - 1 - m_ChooseSoftwareText - 1 - - - protected - 1 - - Resizable - 1 - - - ; ; forward_declare - 0 - - - - - -1 - - - - 5 - wxEXPAND | wxALL - 1 - - 1 - 1 - 1 - 1 - - - - - - wxSYS_COLOUR_ACTIVECAPTION - - 1 - 0 - 1 - - 1 - 0 - Dock - 0 - Left - 1 - - 1 - - 0 - 0 - wxID_ANY - - 0 - - - 0 - - 1 - m_panel11 - 1 - - - protected - 1 - - Resizable - 1 - - ; ; forward_declare - 0 - - - - wxTAB_TRAVERSAL - - - bSizer6 - wxHORIZONTAL - none - - 5 - wxALL - 0 - - 1 - 1 - 1 - 1 - - - - - 0 - - - - - 1 - 0 - 1 - - 1 - - 0 - 0 - - Dock - 0 - Left - 1 - - 1 - - - 0 - 0 - wxID_ANY - RHK - - 0 - - 0 - - - 0 - - 1 - m_RHKbutton - 1 - - - protected - 1 - - - - Resizable - 1 - - - ; ; forward_declare - 0 - - - wxFILTER_NONE - wxDefaultValidator - - - - - OnRHK - - - - 5 - wxALL - 0 - - 1 - 1 - 1 - 1 - - - - - 0 - - - - - 1 - 0 - 1 - - 1 - - 0 - 0 - - Dock - 0 - Left - 1 - - 1 - - - 0 - 0 - wxID_ANY - CreaTec - - 0 - - 0 - - - 0 - - 1 - m_CreaTecbutton - 1 - - - protected - 1 - - - - Resizable - 1 - - - ; ; forward_declare - 0 - - - wxFILTER_NONE - wxDefaultValidator - - - - - OnCreaTec - - - - 5 - wxALL - 0 - - 1 - 1 - 1 - 1 - - - - - 0 - - - - - 1 - 0 - 1 - - 1 - - 0 - 0 - - Dock - 0 - Left - 1 - - 1 - - - 0 - 0 - wxID_ANY - SXM - - 0 - - 0 - - - 0 - - 1 - m_SXMbutton - 1 - - - protected - 1 - - - - Resizable - 1 - - - ; ; forward_declare - 0 - - - wxFILTER_NONE - wxDefaultValidator - - - - - OnSXM - - - - - - - - 0 wxAUI_MGR_DEFAULT wxSYS_COLOUR_SCROLLBAR @@ -1136,7 +707,7 @@ - + 1 wxBOTH 0 @@ -1406,11 +977,11 @@ - + 5 wxEXPAND 1 - + 2 wxBOTH 0 @@ -1422,11 +993,11 @@ none 0 0 - + 5 wxEXPAND | wxALL 1 - + 1 1 1 @@ -1477,7 +1048,7 @@ wxTAB_TRAVERSAL - + 2 wxBOTH 0 @@ -1489,11 +1060,11 @@ none 0 0 - + 5 wxEXPAND | wxALL 1 - + 1 1 1 @@ -1612,11 +1183,11 @@ - + 5 wxEXPAND | wxALL 1 - + 1 1 1 @@ -2534,7 +2105,7 @@ - + 0 wxAUI_MGR_DEFAULT wxSYS_COLOUR_INACTIVECAPTION @@ -2561,7 +2132,7 @@ - + 1 wxBOTH 0 @@ -2632,11 +2203,11 @@ wxVSCROLL - + 5 wxALL|wxEXPAND 1 - + 1 1 1 @@ -2687,16 +2258,16 @@ wxTAB_TRAVERSAL - + bSizer6 wxVERTICAL none - + 5 wxALIGN_CENTER_HORIZONTAL|wxALL 1 - + 1 1 1 @@ -2747,7 +2318,7 @@ wxTAB_TRAVERSAL - + 0 wxBOTH