From 2caa18988f29324d523b0b86a9f0b18a5888c8c9 Mon Sep 17 00:00:00 2001 From: bvr87662 Date: Sun, 15 Dec 2024 14:23:01 +0000 Subject: [PATCH 01/88] Add .gitattributes and .gitignore. --- .gitattributes | 63 +++++++++ .gitignore | 363 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 426 insertions(+) create mode 100644 .gitattributes create mode 100644 .gitignore diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..1ff0c42 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,63 @@ +############################################################################### +# Set default behavior to automatically normalize line endings. +############################################################################### +* text=auto + +############################################################################### +# Set default behavior for command prompt diff. +# +# This is need for earlier builds of msysgit that does not have it on by +# default for csharp files. +# Note: This is only used by command line +############################################################################### +#*.cs diff=csharp + +############################################################################### +# Set the merge driver for project and solution files +# +# Merging from the command prompt will add diff markers to the files if there +# are conflicts (Merging from VS is not affected by the settings below, in VS +# the diff markers are never inserted). Diff markers may cause the following +# file extensions to fail to load in VS. An alternative would be to treat +# these files as binary and thus will always conflict and require user +# intervention with every merge. To do so, just uncomment the entries below +############################################################################### +#*.sln merge=binary +#*.csproj merge=binary +#*.vbproj merge=binary +#*.vcxproj merge=binary +#*.vcproj merge=binary +#*.dbproj merge=binary +#*.fsproj merge=binary +#*.lsproj merge=binary +#*.wixproj merge=binary +#*.modelproj merge=binary +#*.sqlproj merge=binary +#*.wwaproj merge=binary + +############################################################################### +# behavior for image files +# +# image files are treated as binary by default. +############################################################################### +#*.jpg binary +#*.png binary +#*.gif binary + +############################################################################### +# diff behavior for common document formats +# +# Convert binary document formats to text before diffing them. This feature +# is only available from the command line. Turn it on by uncommenting the +# entries below. +############################################################################### +#*.doc diff=astextplain +#*.DOC diff=astextplain +#*.docx diff=astextplain +#*.DOCX diff=astextplain +#*.dot diff=astextplain +#*.DOT diff=astextplain +#*.pdf diff=astextplain +#*.PDF diff=astextplain +#*.rtf diff=astextplain +#*.RTF diff=astextplain diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..9491a2f --- /dev/null +++ b/.gitignore @@ -0,0 +1,363 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore + +# User-specific files +*.rsuser +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Mono auto generated files +mono_crash.* + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +[Ww][Ii][Nn]32/ +[Aa][Rr][Mm]/ +[Aa][Rr][Mm]64/ +bld/ +[Bb]in/ +[Oo]bj/ +[Oo]ut/ +[Ll]og/ +[Ll]ogs/ + +# Visual Studio 2015/2017 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# Visual Studio 2017 auto generated files +Generated\ Files/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUnit +*.VisualState.xml +TestResult.xml +nunit-*.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET Core +project.lock.json +project.fragment.lock.json +artifacts/ + +# ASP.NET Scaffolding +ScaffoldingReadMe.txt + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio +*_i.c +*_p.c +*_h.h +*.ilk +*.meta +*.obj +*.iobj +*.pch +*.pdb +*.ipdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*_wpftmp.csproj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# Visual Studio Trace Files +*.e2e + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Coverlet is a free, cross platform Code Coverage Tool +coverage*.json +coverage*.xml +coverage*.info + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# NuGet Symbol Packages +*.snupkg +# The packages folder can be ignored because of Package Restore +**/[Pp]ackages/* +# except build/, which is used as an MSBuild target. +!**/[Pp]ackages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/[Pp]ackages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt +*.appx +*.appxbundle +*.appxupload + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!?*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm +ServiceFabricBackup/ +*.rptproj.bak + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings +*.rptproj.rsuser +*- [Bb]ackup.rdl +*- [Bb]ackup ([0-9]).rdl +*- [Bb]ackup ([0-9][0-9]).rdl + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# CodeRush personal settings +.cr/personal + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# OpenCover UI analysis results +OpenCover/ + +# Azure Stream Analytics local run output +ASALocalRun/ + +# MSBuild Binary and Structured Log +*.binlog + +# NVidia Nsight GPU debugger configuration file +*.nvuser + +# MFractors (Xamarin productivity tool) working folder +.mfractor/ + +# Local History for Visual Studio +.localhistory/ + +# BeatPulse healthcheck temp database +healthchecksdb + +# Backup folder for Package Reference Convert tool in Visual Studio 2017 +MigrationBackup/ + +# Ionide (cross platform F# VS Code tools) working folder +.ionide/ + +# Fody - auto-generated XML schema +FodyWeavers.xsd \ No newline at end of file From a4d8445c51e5f1d7f97143925b671efe7573262d Mon Sep 17 00:00:00 2001 From: bvr87662 Date: Sun, 15 Dec 2024 14:23:07 +0000 Subject: [PATCH 02/88] Add project files. --- App.config | 48 + App.config.py | 20 + App.py | 15 + App.xaml | 8 + App.xaml.cs | 17 + Dialogs/PreviewDialog.py | 62 + Dialogs/PreviewDialog.xaml | 23 + Dialogs/PreviewDialog.xaml.cs | 40 + EstimationPanel.py | 62 + EstimationPanel.xaml | 40 + EstimationPanel.xaml.cs | 38 + IO/CsvExporter.cs | 93 + IO/CsvExporter.py | 39 + IO/ExcelIO.cs | 352 ++ IO/ExcelIO.py | 84 + MainWindow.py | 185 + MainWindow.xaml | 62 + MainWindow.xaml.cs | 197 + Models/A2Setting.cs | 24 + Models/A2Setting.py | 19 + Models/DataCollection.cs | 26 + Models/DataCollection.py | 22 + Models/Estimation.cs | 115 + Models/Estimation.py | 89 + Models/Experiment.cs | 290 ++ Models/Experiment.py | 223 + Models/ExperimentSettings.cs | 292 ++ Models/ExperimentSettings.py | 187 + Models/Geometry.cs | 25 + Models/Geometry.py | 20 + Models/InstrumentInfo.cs | 98 + Models/InstrumentInfo.py | 103 + Models/RunOrder.cs | 25 + Models/RunOrder.py | 20 + Models/WaitForUnits.cs | 25 + Models/WaitForUnits.py | 20 + Properties/AssemblyInfo.cs | 55 + Properties/AssemblyInfo.py | 23 + Properties/Resources.Designer.cs | 63 + Properties/Resources.Designer.py | 31 + Properties/Resources.ini | 16 + Properties/Resources.resx | 117 + Properties/Settings.Designer.cs | 158 + Properties/Settings.ini | 13 + Properties/Settings.py | 119 + Properties/Settings.settings | 39 + SANS_Icon.ico | Bin 0 -> 59390 bytes SANS_Script_GUI_v4.csproj | 185 + SANS_Script_GUI_v4.csproj.py | 175 + SANS_Script_GUI_v4.csproj.user.py | 16 + SANS_Script_GUI_v4.xml | 2 + SANS_Script_Writer/SANS_Script_Writer.sln | 20 + .../SANS_Script_Writer/SANS_Script_Writer.py | 1 + .../SANS_Script_Writer.pyproj | 35 + Scripting/GeniePythonScriptGenerator.cs | 540 +++ Scripting/GeniePythonScriptGenerator.py | 256 ++ Scripting/IScriptGenerator.cs | 11 + Scripting/IScriptGenerator.py | 10 + Scripting/OpenGenieScriptGenerator.cs | 627 +++ Scripting/OpenGenieScriptGenerator.py | 309 ++ SettingsPanel.py | 98 + SettingsPanel.xaml | 72 + SettingsPanel.xaml.cs | 47 + Transcript_GitCopilot.txt | 3937 +++++++++++++++++ ViewModels/A2Setting.py | 6 + ViewModels/DataCollection.py | 4 + ViewModels/DataGridVM.cs | 41 + ViewModels/DataGridVM.py | 47 + ViewModels/EstimationPanelVM.cs | 24 + ViewModels/EstimationPanelVM.py | 28 + ViewModels/Geometry.py | 4 + ViewModels/InstrumentVM.cs | 179 + ViewModels/InstrumentVM.py | 87 + ViewModels/RunOrder.py | 4 + ViewModels/SettingsPanelVM.cs | 87 + ViewModels/SettingsPanelVM.py | 75 + ViewModels/WaitForUnits.py | 4 + app_config.ini | 13 + 78 files changed, 10586 insertions(+) create mode 100644 App.config create mode 100644 App.config.py create mode 100644 App.py create mode 100644 App.xaml create mode 100644 App.xaml.cs create mode 100644 Dialogs/PreviewDialog.py create mode 100644 Dialogs/PreviewDialog.xaml create mode 100644 Dialogs/PreviewDialog.xaml.cs create mode 100644 EstimationPanel.py create mode 100644 EstimationPanel.xaml create mode 100644 EstimationPanel.xaml.cs create mode 100644 IO/CsvExporter.cs create mode 100644 IO/CsvExporter.py create mode 100644 IO/ExcelIO.cs create mode 100644 IO/ExcelIO.py create mode 100644 MainWindow.py create mode 100644 MainWindow.xaml create mode 100644 MainWindow.xaml.cs create mode 100644 Models/A2Setting.cs create mode 100644 Models/A2Setting.py create mode 100644 Models/DataCollection.cs create mode 100644 Models/DataCollection.py create mode 100644 Models/Estimation.cs create mode 100644 Models/Estimation.py create mode 100644 Models/Experiment.cs create mode 100644 Models/Experiment.py create mode 100644 Models/ExperimentSettings.cs create mode 100644 Models/ExperimentSettings.py create mode 100644 Models/Geometry.cs create mode 100644 Models/Geometry.py create mode 100644 Models/InstrumentInfo.cs create mode 100644 Models/InstrumentInfo.py create mode 100644 Models/RunOrder.cs create mode 100644 Models/RunOrder.py create mode 100644 Models/WaitForUnits.cs create mode 100644 Models/WaitForUnits.py create mode 100644 Properties/AssemblyInfo.cs create mode 100644 Properties/AssemblyInfo.py create mode 100644 Properties/Resources.Designer.cs create mode 100644 Properties/Resources.Designer.py create mode 100644 Properties/Resources.ini create mode 100644 Properties/Resources.resx create mode 100644 Properties/Settings.Designer.cs create mode 100644 Properties/Settings.ini create mode 100644 Properties/Settings.py create mode 100644 Properties/Settings.settings create mode 100644 SANS_Icon.ico create mode 100644 SANS_Script_GUI_v4.csproj create mode 100644 SANS_Script_GUI_v4.csproj.py create mode 100644 SANS_Script_GUI_v4.csproj.user.py create mode 100644 SANS_Script_GUI_v4.xml create mode 100644 SANS_Script_Writer/SANS_Script_Writer.sln create mode 100644 SANS_Script_Writer/SANS_Script_Writer/SANS_Script_Writer.py create mode 100644 SANS_Script_Writer/SANS_Script_Writer/SANS_Script_Writer.pyproj create mode 100644 Scripting/GeniePythonScriptGenerator.cs create mode 100644 Scripting/GeniePythonScriptGenerator.py create mode 100644 Scripting/IScriptGenerator.cs create mode 100644 Scripting/IScriptGenerator.py create mode 100644 Scripting/OpenGenieScriptGenerator.cs create mode 100644 Scripting/OpenGenieScriptGenerator.py create mode 100644 SettingsPanel.py create mode 100644 SettingsPanel.xaml create mode 100644 SettingsPanel.xaml.cs create mode 100644 Transcript_GitCopilot.txt create mode 100644 ViewModels/A2Setting.py create mode 100644 ViewModels/DataCollection.py create mode 100644 ViewModels/DataGridVM.cs create mode 100644 ViewModels/DataGridVM.py create mode 100644 ViewModels/EstimationPanelVM.cs create mode 100644 ViewModels/EstimationPanelVM.py create mode 100644 ViewModels/Geometry.py create mode 100644 ViewModels/InstrumentVM.cs create mode 100644 ViewModels/InstrumentVM.py create mode 100644 ViewModels/RunOrder.py create mode 100644 ViewModels/SettingsPanelVM.cs create mode 100644 ViewModels/SettingsPanelVM.py create mode 100644 ViewModels/WaitForUnits.py create mode 100644 app_config.ini diff --git a/App.config b/App.config new file mode 100644 index 0000000..2f23160 --- /dev/null +++ b/App.config @@ -0,0 +1,48 @@ + + + + +
+ + + + + + + + + LARMOR + + + 11 + + + 11 + + + Flat Plate + + + Alternate - TRANS first + + + 1 + + + 1 + + + False + + + Large = 25, 16 mm + + + Large = 25, 16 mm + + + Events + + + + diff --git a/App.config.py b/App.config.py new file mode 100644 index 0000000..31cf442 --- /dev/null +++ b/App.config.py @@ -0,0 +1,20 @@ +import configparser + +config = configparser.ConfigParser() + +config['DEFAULT'] = { + 'LastInstrument': 'LARMOR', + 'SampleHeight': '11', + 'SampleWidth': '11', + 'Geometry': 'Flat Plate', + 'RunOrder': 'Alternate - TRANS first', + 'NumTrans': '1', + 'NumSans': '1', + 'LoopPerRun': 'False', + 'A2SettingSans': 'Large = 25, 16 mm', + 'A2SettingTrans': 'Large = 25, 16 mm', + 'CollectionMode': 'Events' +} + +with open('app_config.ini', 'w') as configfile: + config.write(configfile) \ No newline at end of file diff --git a/App.py b/App.py new file mode 100644 index 0000000..86d8cb9 --- /dev/null +++ b/App.py @@ -0,0 +1,15 @@ +import tkinter as tk +from MainWindow import MainWindow + +class App: + def __init__(self): + self.root = tk.Tk() + self.main_window = MainWindow(self.root) + self.main_window.pack(fill=tk.BOTH, expand=True) + + def run(self): + self.root.mainloop() + +if __name__ == "__main__": + app = App() + app.run() \ No newline at end of file diff --git a/App.xaml b/App.xaml new file mode 100644 index 0000000..704a2c1 --- /dev/null +++ b/App.xaml @@ -0,0 +1,8 @@ + + + + + diff --git a/App.xaml.cs b/App.xaml.cs new file mode 100644 index 0000000..b99238d --- /dev/null +++ b/App.xaml.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Configuration; +using System.Data; +using System.Linq; +using System.Threading.Tasks; +using System.Windows; + +namespace LOQ_Script_Gui +{ + /// + /// Interaction logic for App.xaml + /// + public partial class App : Application + { + } +} diff --git a/Dialogs/PreviewDialog.py b/Dialogs/PreviewDialog.py new file mode 100644 index 0000000..167bcb8 --- /dev/null +++ b/Dialogs/PreviewDialog.py @@ -0,0 +1,62 @@ +import tkinter as tk +from tkinter import scrolledtext + + + +class PreviewDialog(tk.Toplevel): + def __init__(self, parent): + super().__init__(parent) + self.title("PreviewDialog") + self.geometry("600x500") + + self.grid_rowconfigure(0, weight=1) + self.grid_columnconfigure(0, weight=1) + + # Create a frame for the text area with a border + border_frame = tk.Frame(self, borderwidth=1, relief="solid") + border_frame.grid(row=0, column=0, padx=5, pady=5, sticky="nsew") + + # Create a scrollable text area + self.txt_script = scrolledtext.ScrolledText(border_frame, wrap=tk.WORD) + self.txt_script.pack(fill=tk.BOTH, expand=True) + + self.script_text.insert(tk.END, self.txt_script) + self.script_text.config(state=tk.DISABLED) + # Create a frame for the buttons + button_frame = tk.Frame(self) + button_frame.grid(row=1, column=0, pady=5, sticky="e") + + # Create the Print Script button (initially hidden) + self.btn_print = tk.Button(button_frame, text="Print Script", command=self.print_script) + self.btn_print.pack(side=tk.LEFT, padx=5) + self.btn_print.pack_forget() # Hide the button initially + + # Create the Close button + btn_close = tk.Button(button_frame, text="Close", command=self.close) + btn_close.pack(side=tk.LEFT, padx=5) + + def set_script(self, script): + self.txt_script.delete(1.0, tk.END) + self.txt_script.insert(tk.END, script) + + def print_script(self): + # Implement the print functionality here + pass + def show(self): + self.grab_set() + self.wait_window() + + def close(self): + self.destroy() + +# Example usage +if __name__ == "__main__": + root = tk.Tk() + root.withdraw() # Hide the root window + + dialog = PreviewDialog(root) + + dialog.set_script("This is a preview of the script.") + + #dialog.txt_script.insert(tk.END, "This is a preview of the script.") + dialog.mainloop() \ No newline at end of file diff --git a/Dialogs/PreviewDialog.xaml b/Dialogs/PreviewDialog.xaml new file mode 100644 index 0000000..b85e3c9 --- /dev/null +++ b/Dialogs/PreviewDialog.xaml @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/Dialogs/PreviewDialog.xaml.cs b/Dialogs/PreviewDialog.xaml.cs new file mode 100644 index 0000000..9edb2c7 --- /dev/null +++ b/Dialogs/PreviewDialog.xaml.cs @@ -0,0 +1,40 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Imaging; +using System.Windows.Shapes; +using System.IO; +using System.Windows.Xps.Packaging; +using System.Windows.Documents; + +namespace LOQ_Script_Gui.Dialogs +{ + /// + /// Interaction logic for PreviewDialog.xaml + /// + public partial class PreviewDialog : Window + { + public PreviewDialog() + { + InitializeComponent(); + } + + public void SetScript(string script) + { + txtScript.Text = script; + } + + private void Button_Click(object sender, RoutedEventArgs e) + { + this.Close(); + } + } +} diff --git a/EstimationPanel.py b/EstimationPanel.py new file mode 100644 index 0000000..5f81c01 --- /dev/null +++ b/EstimationPanel.py @@ -0,0 +1,62 @@ +import tkinter as tk +from tkinter import ttk +from ViewModels.EstimationPanelVM import EstimationPanelVM +from Models.Estimation import Estimation + +class EstimationPanel(ttk.Frame): + def __init__(self, parent, estimate = None): + super().__init__(parent) + self.on_calculate_clicked = None + self.estimate = estimate if estimate is not None else Estimation() + self.create_widgets() + + def create_widgets(self): + #self.pack(fill=tk.BOTH, expand=True) + self.grid(sticky="nsew") + + label = ttk.Label(self, text="Estimated Script Time", font=("Helvetica", 16, "bold")) + label.grid(row=0, column=0, columnspan=3, pady=10) + + grid_frame = ttk.Frame(self) + grid_frame.grid(row=1, column=0, columnspan=3, padx=10, pady=10, sticky="nsew") + + + # Est. count rate + ttk.Label(grid_frame, text="Est. count rate:").grid(row=0, column=0, sticky=tk.E) + count_rate_frame = ttk.Frame(grid_frame) + count_rate_frame.grid(row=0, column=1, columnspan=2) + self.txt_count_rate = ttk.Entry(count_rate_frame, width=10, textvariable=self.estimate.count_rate) + self.txt_count_rate.grid(row=0, column=0, padx=5, pady=5) + ttk.Label(count_rate_frame, text="µA/hr").grid(row=0, column=1) + + # Est. time between moves + ttk.Label(grid_frame, text="Est. time between moves:").grid(row=1, column=0, sticky=tk.E) + move_time_frame = ttk.Frame(grid_frame) + move_time_frame.grid(row=1, column=1, columnspan=2) + self.txt_move_time = ttk.Entry(move_time_frame, width=10, textvariable=self.estimate.move_time) + self.txt_move_time.grid(row=0, column=0, padx=5, pady=5) + ttk.Label(move_time_frame, text="seconds").grid(row=0, column=1) + + # Est. script time + ttk.Label(grid_frame, text="Est. script time:").grid(row=2, column=0, sticky=tk.E) + script_time_frame = ttk.Frame(grid_frame) + script_time_frame.grid(row=2, column=1, columnspan=2) + self.txt_estimate = ttk.Entry(script_time_frame, width=15, textvariable=self.estimate.script_time, state='readonly') + self.txt_estimate.grid(row=0, column=0, padx=5, pady=5) + + # Calculate button + self.btn_calculate = ttk.Button(grid_frame, text="Calculate", command=self.btn_calculate_click) + self.btn_calculate.grid(row=3, column=1, columnspan=2, pady=10) + + def btn_calculate_click(self): + if self.on_calculate_clicked: + self.on_calculate_clicked() + + + +if __name__ == "__main__": + root = tk.Tk() + estimate = EstimationPanelVM() + panel = EstimationPanel(root, estimate) + panel.pack(fill=tk.BOTH, expand=True) + root.mainloop() \ No newline at end of file diff --git a/EstimationPanel.xaml b/EstimationPanel.xaml new file mode 100644 index 0000000..214c556 --- /dev/null +++ b/EstimationPanel.xaml @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/EstimationPanel.xaml.cs b/EstimationPanel.xaml.cs new file mode 100644 index 0000000..43a5813 --- /dev/null +++ b/EstimationPanel.xaml.cs @@ -0,0 +1,38 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Imaging; +using System.Windows.Navigation; +using System.Windows.Shapes; + +namespace LOQ_Script_Gui +{ + /// + /// Interaction logic for EstimationPanel.xaml + /// + public partial class EstimationPanel : UserControl + { + public event EventHandler OnCalculateClicked; + + public EstimationPanel() + { + InitializeComponent(); + } + + private void btnCalculate_Click(object sender, RoutedEventArgs e) + { + if (OnCalculateClicked != null) + { + OnCalculateClicked(sender, e); + } + } + } +} diff --git a/IO/CsvExporter.cs b/IO/CsvExporter.cs new file mode 100644 index 0000000..d732c1b --- /dev/null +++ b/IO/CsvExporter.cs @@ -0,0 +1,93 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Collections.ObjectModel; +using System.IO; + +namespace LOQ_Script_Gui +{ + class CsvExporter + { + public static void Export(string file, ObservableCollection runs) + { + using(StreamWriter sw = new StreamWriter(file)) + { + foreach(Experiment exp in runs) + { + // Position + WriteParameter(sw, exp.Position); + + // Trans + WriteParameter(sw, exp.Trans.ToString()); + + // Trans wait + WriteParameter(sw, exp.TransWait); + + // Sans + WriteParameter(sw, exp.Sans.ToString()); + + // Sans wait + WriteParameter(sw, exp.SansWait); + + // Period + WriteParameter(sw, exp.Period); + + // Sample ID + WriteParameter(sw, exp.Sample); + + // Sample ID + WriteParameter(sw, exp.Background); + + // Thickness + WriteParameter(sw, exp.Thickness); + + // Temperature 1 + WriteParameter(sw, exp.Temperature1); + + // Temperature 2 + WriteParameter(sw, exp.Temperature2); + + // Field + WriteParameter(sw, exp.Field); + + // Shear rate 1 + WriteParameter(sw, exp.ShearRate1); + + // Shear rate 2 + WriteParameter(sw, exp.ShearRate2); + + // Shear angle 1 + WriteParameter(sw, exp.ShearAngle1); + + // Shear angle 2 + WriteParameter(sw, exp.ShearAngle2); + + // Pre-command + WriteParameter(sw, exp.PreCommand); + + // Post-command + WriteParameter(sw, exp.PostCommand); + + // RB + WriteParameter(sw, exp.RbNumber); + + sw.WriteLine(); + } + } + } + + private static void WriteParameter(StreamWriter sw, string par) + { + if (!string.IsNullOrWhiteSpace(par)) + { + sw.Write(string.Format("{0}, ", par)); + } + else + { + sw.Write(", "); + } + } + } +} diff --git a/IO/CsvExporter.py b/IO/CsvExporter.py new file mode 100644 index 0000000..7a759ef --- /dev/null +++ b/IO/CsvExporter.py @@ -0,0 +1,39 @@ +import csv +from Models.Experiment import Experiment + +class CsvExporter: + @staticmethod + def export(file, runs): + with open(file, mode='w', newline='') as csvfile: + writer = csv.writer(csvfile) + for exp in runs: + row = [ + exp.position, + str(exp.trans), + exp.trans_wait, + str(exp.sans), + exp.sans_wait, + exp.period, + exp.sample, + exp.background, + exp.thickness, + exp.temperature1, + exp.temperature2, + exp.field, + exp.shear_rate1, + exp.shear_rate2, + exp.shear_angle1, + exp.shear_angle2, + exp.pre_command, + exp.post_command, + exp.rb_number + ] + writer.writerow(row) + + + +if __name__ == "__main__": + runs = [ + Experiment(position="pos1", trans=10, trans_wait="Minutes", sans=20, sans_wait="Minutes", period="1", sample="Sample1", background="Background1", thickness="1.0", temperature1="25", temperature2="30", field="1.5T", shear_rate1="100", shear_rate2="200", shear_angle1="45", shear_angle2="90", pre_command="Pre Command", post_command="Post Command", rb_number="RB12345") + ] + CsvExporter.export('output.csv', runs) \ No newline at end of file diff --git a/IO/ExcelIO.cs b/IO/ExcelIO.cs new file mode 100644 index 0000000..13ed41f --- /dev/null +++ b/IO/ExcelIO.cs @@ -0,0 +1,352 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Data.OleDb; +using System.Data; +using System.IO; +using System.Reflection; +using System.Collections.ObjectModel; +using System.Windows; + +namespace LOQ_Script_Gui +{ + class ExcelIO + { + static List columns = new List() + { + "POSITION", "TRANS", "TRANS_WAIT_FOR", "SANS", "SANS_WAIT_FOR", "PERIOD", "SAMPLE_IDENTIFICATION_STRING", "BACKGROUND", + "THICKNESS", "TEMPERATURE", "TEMPERATURE2", "FIELD", "SHEAR_RATE_1", "SHEAR_RATE_2", "SHEAR_ANGLE_1", "SHEAR_ANGLE_2", + "PRE_COMMAND", "POST_COMMAND", "RB_NUMBER" + }; + + // The last column used in Excel + static string LastColumn = "S"; + + DataSet dataset = new DataSet(); + + public void OpenExcelWorkBook(string filename) + { + // Create connection string variable + // This is the 32 bit office version requiring Microsoft Access Database Engine 2010 Redistributable + String sConnectionString1 = "Provider=Microsoft.Jet.OLEDB.4.0;" + + "Data Source=" + filename + ";" + + "Extended Properties='Excel 8.0;HDR=Yes;IMEX=1;'"; + // if 64 bit office is installed try this one Microsoft Access Database Engine 2016 Redistributable with _X64 + String sConnectionString2 = "Provider=Microsoft.ACE.OLEDB.12.0;" + + "Data Source=" + filename + ";" + + "Mode=Read;Extended Properties='Excel 12.0;HDR=Yes;IMEX=1;'"; + + // Create connection object + OleDbConnection conn = new OleDbConnection(sConnectionString1); + OleDbConnection conn2 = new OleDbConnection(sConnectionString2); + + try + { + // Open connection + conn.Open(); + + String[] names = GetWorkSheetNames(conn); + // Create new OleDbCommand to return data from the first worksheet + OleDbCommand cmd = new OleDbCommand(@"SELECT * FROM [" + names[0] + "]", conn); + + // Create new OleDbDataAdapter that is used to build a DataSet + // based on the preceding SQL SELECT statement + OleDbDataAdapter adapter = new OleDbDataAdapter(); + + // Pass the Select command to the adapter + adapter.SelectCommand = cmd; + + // Fill the DataSet with the information from the worksheet + adapter.Fill(dataset, "XLData"); + + } + catch (Exception ex) + { + //MessageBox.Show(ex.Message, "SANS Script - Error", MessageBoxButton.OK, MessageBoxImage.Error); + try + { + // Open connection + conn2.Open(); + + String[] names = GetWorkSheetNames(conn2); + // Create new OleDbCommand to return data from the first worksheet + OleDbCommand cmd = new OleDbCommand(@"SELECT * FROM [" + names[0] + "]", conn2); + + // Create new OleDbDataAdapter that is used to build a DataSet + // based on the preceding SQL SELECT statement + OleDbDataAdapter adapter = new OleDbDataAdapter(); + + // Pass the Select command to the adapter + adapter.SelectCommand = cmd; + + // Fill the DataSet with the information from the worksheet + adapter.Fill(dataset, "XLData"); + } + catch (Exception ex1) + { + dataset = null; + MessageBox.Show(ex1.Message, "SANS Script - Error", MessageBoxButton.OK, MessageBoxImage.Error); + throw new IOException("Could not open Excel spreadsheet"); + + } + } + finally + { + // Clean up objects. + conn.Close(); + conn.Dispose(); + conn2.Close(); + conn2.Dispose(); + } + } + + public List ExtractExperiments() + { + List experiments = new List(); + + if (dataset != null) + { + if (dataset.Tables != null && dataset.Tables.Count > 0) + { + if (dataset.Tables[0].Rows != null && dataset.Tables[0].Rows.Count > 0) + { + // Loop through the rows and create an experiment for each + foreach (DataRow dr in dataset.Tables[0].Rows) + { + Experiment exp = new Experiment(); + + // Position + if (!string.IsNullOrWhiteSpace(dr[0].ToString())) + { + exp.Position = dr[0].ToString(); + } + + // Trans + if (!string.IsNullOrWhiteSpace(dr[1].ToString())) + { + exp.Trans = CastToDouble(dr[1].ToString()); + } + + // Trans wait + if (!string.IsNullOrWhiteSpace(dr[2].ToString())) + { + exp.TransWait = dr[2].ToString(); + } + + // Sans + if (!string.IsNullOrWhiteSpace(dr[3].ToString())) + { + exp.Sans = CastToDouble(dr[3].ToString()); + } + + // Sans wait + if (!string.IsNullOrWhiteSpace(dr[4].ToString())) + { + exp.SansWait = dr[4].ToString(); + } + + // Period + if (!string.IsNullOrWhiteSpace(dr[5].ToString())) + { + exp.Period = dr[5].ToString(); + } + + // Sample ID + if (!string.IsNullOrWhiteSpace(dr[6].ToString())) + { + exp.Sample = dr[6].ToString(); + } + + // Background + if (!string.IsNullOrWhiteSpace(dr[7].ToString())) + { + exp.Background = dr[7].ToString(); + } + + // Thickness + if (!string.IsNullOrWhiteSpace(dr[8].ToString())) + { + exp.Thickness = dr[8].ToString(); + } + + // Temperature 1 + if (!string.IsNullOrWhiteSpace(dr[9].ToString())) + { + exp.Temperature1 = dr[9].ToString(); + } + + // Temperature 2 + if (!string.IsNullOrWhiteSpace(dr[10].ToString())) + { + exp.Temperature2 = dr[10].ToString(); + } + + // Field + if (!string.IsNullOrWhiteSpace(dr[11].ToString())) + { + exp.Field = dr[11].ToString(); + } + + // Shear rate 1 + if (!string.IsNullOrWhiteSpace(dr[12].ToString())) + { + exp.ShearRate1 = dr[12].ToString(); + } + + // Shear rate 2 + if (!string.IsNullOrWhiteSpace(dr[13].ToString())) + { + exp.ShearRate2 = dr[13].ToString(); + } + + // Shear angle 1 + if (!string.IsNullOrWhiteSpace(dr[14].ToString())) + { + exp.ShearAngle1 = dr[14].ToString(); + } + + // Shear angle 2 + if (!string.IsNullOrWhiteSpace(dr[15].ToString())) + { + exp.ShearAngle2 = dr[15].ToString(); + } + + // Pre-command + if (!string.IsNullOrWhiteSpace(dr[16].ToString())) + { + exp.PreCommand = dr[16].ToString(); + } + + // Post-command + if (!string.IsNullOrWhiteSpace(dr[17].ToString())) + { + exp.PostCommand = dr[17].ToString(); + } + + // RB + if (!string.IsNullOrWhiteSpace(dr[18].ToString())) + { + exp.RbNumber = dr[18].ToString(); + } + + experiments.Add(exp); + } + + } + } + } + + return experiments; + } + + private static double CastToDouble(string val) + { + double result; + if (Double.TryParse(val, out result)) + { + return result; + } + + return 0.0; + } + + private static String[] GetWorkSheetNames(OleDbConnection conn) + { + System.Data.DataTable dt = null; + + try + { + // Get the data table containg the schema guid + dt = conn.GetOleDbSchemaTable(OleDbSchemaGuid.Tables, null); + + if (dt == null) + { + return null; + } + + String[] excelSheets = new String[dt.Rows.Count]; + int i = 0; + + // Add the sheet name to the string array + foreach (DataRow row in dt.Rows) + { + excelSheets[i] = row["TABLE_NAME"].ToString(); + i++; + } + + return excelSheets; + } + catch (Exception ex) + { + throw (ex); + } + finally + { + if (dt != null) + { + dt.Dispose(); + } + } + } + + public void ExportDataToExcel(ObservableCollection runs) + { + Microsoft.Office.Interop.Excel.Application xlApp; + Microsoft.Office.Interop.Excel.Workbook xlBook; + Microsoft.Office.Interop.Excel.Worksheet xlSheet; + Microsoft.Office.Interop.Excel.Range xlRange; + + try + { + // Start Excel + xlApp = new Microsoft.Office.Interop.Excel.Application(); + xlApp.Visible = true; + + // Create a workbook + xlBook = xlApp.Workbooks.Add(Missing.Value); + + // Set to page + xlSheet = (Microsoft.Office.Interop.Excel.Worksheet)xlBook.ActiveSheet; + + // Create headings + for (int i = 0; i < columns.Count; ++i) + { + xlSheet.Cells[1, i + 1] = columns[i]; + } + + xlSheet.get_Range("A1", LastColumn + "1").Font.Bold = true; + + // Enter data + for (int i = 0; i < runs.Count; ++i) + { + Experiment exp = runs[i]; + List data = new List() + { + exp.Position, exp.Trans.ToString(), exp.TransWait, exp.Sans.ToString(), exp.SansWait, + exp.Period, exp.Sample, exp.Background, exp.Thickness, exp.Temperature1, exp.Temperature2, exp.Field, + exp.ShearRate1, exp.ShearRate2, exp.ShearAngle1, exp.ShearAngle2, exp.PreCommand, + exp.PostCommand, exp.RbNumber + }; + + int row = i + 2; + var range = xlSheet.get_Range("A" + row, LastColumn + row); + range.Value = data.ToArray(); + } + + //Autofit columns + xlRange = xlSheet.get_Range("A1", LastColumn + "1"); + xlRange.EntireColumn.AutoFit(); + + //Detach + xlApp.UserControl = true; + + } + catch (Exception err) + { + throw err; + } + } + } +} diff --git a/IO/ExcelIO.py b/IO/ExcelIO.py new file mode 100644 index 0000000..8b33798 --- /dev/null +++ b/IO/ExcelIO.py @@ -0,0 +1,84 @@ +import pandas as pd +from tkinter import messagebox +from Models.Experiment import Experiment + +class ExcelIO: + columns = [ + "POSITION", "TRANS", "TRANS_WAIT_FOR", "SANS", "SANS_WAIT_FOR", "PERIOD", "SAMPLE_IDENTIFICATION_STRING", "BACKGROUND", + "THICKNESS", "TEMPERATURE", "TEMPERATURE2", "FIELD", "SHEAR_RATE_1", "SHEAR_RATE_2", "SHEAR_ANGLE_1", "SHEAR_ANGLE_2", + "PRE_COMMAND", "POST_COMMAND", "RB_NUMBER" + ] + + + + def __init__(self): + self.dataset = None + + def open_excel_workbook(self, filename): + try: + self.dataset = pd.read_excel(filename, sheet_name=None) + except Exception as ex: + messagebox.showerror("SANS Script - Error", str(ex)) + raise IOError("Could not open Excel spreadsheet") + + + def extract_experiments(self): + experiments = [] + if self.dataset is not None: + for sheet_name, df in self.dataset.items(): + for _, row in df.iterrows(): + exp = Experiment( + position=row.get("POSITION", ""), + trans=self.cast_to_double(row.get("TRANS", 0)), + trans_wait=row.get("TRANS_WAIT_FOR", ""), + sans=self.cast_to_double(row.get("SANS", 0)), + sans_wait=row.get("SANS_WAIT_FOR", ""), + period=row.get("PERIOD", ""), + sample=row.get("SAMPLE_IDENTIFICATION_STRING", ""), + background=row.get("BACKGROUND", ""), + thickness=row.get("THICKNESS", ""), + temperature1=row.get("TEMPERATURE", ""), + temperature2=row.get("TEMPERATURE2", ""), + field=row.get("FIELD", ""), + shear_rate1=row.get("SHEAR_RATE_1", ""), + shear_rate2=row.get("SHEAR_RATE_2", ""), + shear_angle1=row.get("SHEAR_ANGLE_1", ""), + shear_angle2=row.get("SHEAR_ANGLE_2", ""), + pre_command=row.get("PRE_COMMAND", ""), + post_command=row.get("POST_COMMAND", ""), + rb_number=row.get("RB_NUMBER", "") + ) + experiments.append(exp) + return experiments + + @staticmethod + def cast_to_double(val): + try: + return float(val) + except (ValueError, TypeError): + return 0.0 + + + def export_data_to_excel(self, runs, filename): + data = [] + for exp in runs: + data.append([ + exp.position, exp.trans, exp.trans_wait, exp.sans, exp.sans_wait, + exp.period, exp.sample, exp.background, exp.thickness, exp.temperature1, exp.temperature2, exp.field, + exp.shear_rate1, exp.shear_rate2, exp.shear_angle1, exp.shear_angle2, exp.pre_command, + exp.post_command, exp.rb_number + ]) + df = pd.DataFrame(data, columns=self.columns) + df.to_excel(filename, index=False) + + +if __name__ == "__main__": + runs = [ + Experiment(position="pos1", trans=10, trans_wait="Minutes", sans=20, sans_wait="Minutes", period="1", sample="Sample1", background="Background1", thickness="1.0", temperature1="25", temperature2="30", field="1.5T", shear_rate1="100", shear_rate2="200", shear_angle1="45", shear_angle2="90", pre_command="Pre Command", post_command="Post Command", rb_number="RB12345") + ] + excel_io = ExcelIO() + excel_io.export_data_to_excel(runs, 'output.xlsx') + excel_io.open_excel_workbook('output.xlsx') + experiments = excel_io.extract_experiments() + for exp in experiments: + print(exp.__dict__) \ No newline at end of file diff --git a/MainWindow.py b/MainWindow.py new file mode 100644 index 0000000..9f064e1 --- /dev/null +++ b/MainWindow.py @@ -0,0 +1,185 @@ +import tkinter as tk +from tkinter import ttk, filedialog, messagebox +from tkinter.scrolledtext import ScrolledText +import pandas as pd +from Models.DataCollection import DataCollection +from Models.RunOrder import RunOrder +from ViewModels.DataGridVM import DataGridVM +from ViewModels.SettingsPanelVM import SettingsPanelVM +from ViewModels.InstrumentVM import InstrumentVM +from Models.InstrumentInfo import InstrumentInfo +from Models.Estimation import Estimation +from IO.ExcelIO import ExcelIO +from IO.CsvExporter import CsvExporter +from Dialogs.PreviewDialog import PreviewDialog +from SettingsPanel import SettingsPanel +from EstimationPanel import EstimationPanel + + +class MainWindow(tk.Frame): + def __init__(self,parent): + super().__init__(parent) + self.parent = parent + self.parent.title("SANS Script GUI") + self.parent.geometry("800x600") + + self.data = DataGridVM() + self.inst_info = InstrumentInfo() + self.settings = SettingsPanelVM(self.inst_info) + self.estimate = Estimation() + self.instrument = InstrumentVM(self.settings.experiment, self.estimate) + + self.create_widgets() + self.load_last_instrument() + + def create_widgets(self): + #self.data_grid = ttk.Treeview(self) + self.grid_columnconfigure(1, weight=1) + self.grid_rowconfigure(0, weight=1) + + + + + + #self.pnl_settings.pack(fill=tk.X, expand=True, padx=5, pady=5) + # Data Grid + self.data_grid = ttk.Treeview(self, columns=("POSITION", "TRANS", "TRANS__WAIT", "SANS", "SANS__WAIT", "PERIOD", "SAMPLE__IDENTIFICATION__STRING", "BACKGROUND", "THICKNESS", "TEMPERATURE", "TEMPERATURE2", "FIELD", "SHEAR__RATE__1", "SHEAR__RATE__2", "SHEAR__ANGLE__1", "SHEAR__ANGLE__2", "PRE__COMMAND", "POST__COMMAND", "RB__NUMBER"), show="headings") + #self.pnl_settings.pack(fill=tk.X, expand=True, padx=5, pady=5) + self.data_grid.grid(row=0, column=1, rowspan=4, sticky="nsew", padx=5, pady=5) + for col in self.data_grid["columns"]: + self.data_grid.heading(col, text=col.replace("__", " ")) + # Settings Panel + self.pnl_settings = SettingsPanel(self, self.settings) + self.pnl_settings.grid(row=0, column=0, sticky="ew") + self.pnl_settings.on_preview_clicked = self.pnl_settings_on_preview_clicked + self.pnl_settings.on_write_clicked = self.pnl_settings_on_write_clicked + # Estimation Panel + self.pnl_estimate = EstimationPanel(self, self.estimate) + self.pnl_estimate.grid(row=1, column=0, sticky="ew") + self.pnl_estimate.on_calculate_clicked = self.pnl_estimate_on_calculate_clicked + # Buttons + self.btn_create_new_table = ttk.Button(self, text="Create New Table", command=self.btn_create_new_table_click) + self.btn_create_new_table.grid(row=2, column=0, padx=5, pady=5, sticky="w") + + + self.btn_open_excel = ttk.Button(self, text="Open Excel", command=self.btn_open_excel_click) + self.btn_open_excel.grid(row=3, column=0, padx=5, pady=5, sticky="w") + + + + + + self.mnu_main = tk.Menu(self.parent) + self.parent.config(menu=self.mnu_main) + + file_menu = tk.Menu(self.mnu_main, tearoff=0) + self.mnu_main.add_cascade(label="File", menu=file_menu) + file_menu.add_command(label="Export to Excel", command=self.mnu_export_to_excel_click) + file_menu.add_command(label="Save as CSV", command=self.mnu_save_csv_click) + file_menu.add_separator() + file_menu.add_command(label="Exit", command=self.mnu_exit_click) + + instrument_menu = tk.Menu(self.mnu_main, tearoff=0) + self.mnu_main.add_cascade(label="Instrument", menu=instrument_menu) + self.is_larmor = tk.BooleanVar() + self.is_loq = tk.BooleanVar() + self.is_sans2d = tk.BooleanVar() + self.is_zoom = tk.BooleanVar() + instrument_menu.add_checkbutton(label="LARMOR", variable=self.is_larmor) + instrument_menu.add_checkbutton(label="LOQ", variable=self.is_loq) + instrument_menu.add_checkbutton(label="SANS2D", variable=self.is_sans2d) + instrument_menu.add_checkbutton(label="ZOOM", variable=self.is_zoom) + + + + + + + + def load_last_instrument(self): + inst = "LARMOR" # Replace with actual settings retrieval + if inst == "LARMOR": + self.instrument.is_larmor = True + elif inst == "LOQ": + self.instrument.is_loq = True + elif inst == "SANS2D": + self.instrument.is_sans2d = True + elif inst == "ZOOM": + self.instrument.is_zoom = True + else: + self.instrument.is_larmor = True + + def pnl_settings_on_write_clicked(self): + filetypes = [("genie_python Files", "*.py")] if self.instrument.is_larmor or self.instrument.is_loq or self.instrument.is_zoom or self.instrument.is_sans2d else [("Open Genie Files", "*.gcl")] + filepath = filedialog.asksaveasfilename(defaultextension=filetypes[0][1], filetypes=filetypes) + if filepath: + scriptname = filepath.split('/')[-1].replace(" ", "_").rsplit('.', 1)[0] + script = self.inst_info.script_generator.generate_script(self.data.runs, self.settings.experiment, scriptname) + with open(filepath, 'w') as file: + file.write(script) + + def pnl_settings_on_preview_clicked(self): + script = self.inst_info.script_generator.generate_script(self.data.runs, self.settings.experiment, "my_script") + preview_dialog = PreviewDialog(self, script) + preview_dialog.show() + + def pnl_estimate_on_calculate_clicked(self): + self.estimate.script_time = self.inst_info.script_generator.estimate_run_time(self.data.runs, self.settings.experiment, self.estimate) + + def btn_create_new_table_click(self): + self.data.runs.clear() + + def btn_open_excel_click(self): + filepath = filedialog.askopenfilename(defaultextension=".xlsx", filetypes=[("Excel Files", "*.xls;*.xlsx")]) + if filepath: + try: + excel = ExcelIO() + excel.open_excel_workbook(filepath) + exps = excel.extract_experiments() + self.data.runs = exps + except Exception as err: + messagebox.showerror("SANS Script - Error", "Could not open Excel spreadsheet") + + def mnu_exit_click(self): + self.destroy() + + def mnu_export_to_excel_click(self): + excel = ExcelIO() + filepath = filedialog.asksaveasfilename(defaultextension=".xls", filetypes=[("Exel File", "*.xls")]) + excel.export_data_to_excel(filepath,self.data.runs) + + def mnu_save_csv_click(self): + filepath = filedialog.asksaveasfilename(defaultextension=".csv", filetypes=[("CSV File", "*.csv")]) + if filepath: + try: + CsvExporter.export(filepath, self.data.runs) + except Exception as err: + messagebox.showerror("SANS Script - Error", "Could not save as CSV") + + def on_close(self): + if self.instrument.is_larmor: + last_instrument = "LARMOR" + elif self.instrument.is_loq: + last_instrument = "LOQ" + elif self.instrument.is_sans2d: + last_instrument = "SANS2D" + elif self.instrument.is_zoom: + last_instrument = "ZOOM" + else: + last_instrument = "LARMOR" + # Save last_instrument to settings + self.destroy() + + + + + + +if __name__ == "__main__": + root = tk.Tk() + app = MainWindow(root) + app.grid(sticky="nsew") + root.grid_rowconfigure(0, weight=1) + root.grid_columnconfigure(0, weight=1) + app.protocol("WM_DELETE_WINDOW", app.on_close) + app.mainloop() \ No newline at end of file diff --git a/MainWindow.xaml b/MainWindow.xaml new file mode 100644 index 0000000..bb398d7 --- /dev/null +++ b/MainWindow.xaml @@ -0,0 +1,62 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MainWindow.xaml.cs b/MainWindow.xaml.cs new file mode 100644 index 0000000..03ba0f6 --- /dev/null +++ b/MainWindow.xaml.cs @@ -0,0 +1,197 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Imaging; +using System.Windows.Navigation; +using System.Windows.Shapes; +using System.Collections.ObjectModel; +using System.IO; + +namespace LOQ_Script_Gui +{ + public partial class MainWindow : Window + { + DataGridVM data = new DataGridVM(); + SettingsPanelVM settings; + Estimation estimate = new Estimation(); + InstrumentInfo instInfo; + InstrumentVM instrument; + + public MainWindow() + { + InitializeComponent(); + + instInfo = new InstrumentInfo(); + settings = new SettingsPanelVM(instInfo); + + dataGrid.DataContext = data; + + pnlSettings.DataContext = settings; + pnlSettings.OnPreviewClicked += pnlSettings_OnPreviewClicked; + pnlSettings.OnWriteClicked += pnlSettings_OnWriteClicked; + + pnlEstimate.DataContext = estimate; + pnlEstimate.OnCalculateClicked += pnlEstimate_OnCalculateClicked; + + instrument = new InstrumentVM(settings.Experiment, estimate); + mnuMain.DataContext = instrument; + + string inst = Properties.Settings.Default.LastInstrument; + switch (inst) + { + case "LARMOR": + instrument.IsLarmor = true; + break; + case "LOQ": + instrument.IsLoq = true; + break; + case "SANS2D": + instrument.IsSans2d = true; + break; + case "ZOOM": + instrument.IsZoom = true; + break; + default: + instrument.IsLarmor = true; + break; + } + } + + void pnlSettings_OnWriteClicked(object sender, EventArgs e) + { + Microsoft.Win32.SaveFileDialog dlg = new Microsoft.Win32.SaveFileDialog(); + + if (instrument.IsLarmor || instrument.IsLoq || instrument.IsZoom || instrument.IsSans2d) + { + dlg.DefaultExt = ".py"; + dlg.Filter = "genie_python Files|*.py"; + } + else + { + dlg.DefaultExt = ".gcl"; + dlg.Filter = "Open Genie Files|*.gcl"; + } + + if ((bool)dlg.ShowDialog()) + { + // Use the filename provided to set the name of the script, this will be ignored by the opengenie generator + string[] filepath = dlg.FileName.Split('\\'); + int lenarr = filepath.Count(); + string scriptname = filepath[lenarr - 1]; + lenarr = scriptname.Length; + scriptname = scriptname.Substring(0, lenarr - 3); + // check for spaces and replace with underscores. + scriptname = scriptname.Replace(" ", "_"); + + string script = InstrumentInfo.ScriptGenerator.GenerateScript(data.Runs, settings.Experiment, scriptname); + + using (StreamWriter sw = new StreamWriter(dlg.FileName)) + { + sw.Write(script); + } + } + } + + void pnlSettings_OnPreviewClicked(object sender, EventArgs e) + { + string script = InstrumentInfo.ScriptGenerator.GenerateScript(data.Runs, settings.Experiment,"my_script"); + Dialogs.PreviewDialog preview = new Dialogs.PreviewDialog(); + preview.SetScript(script); + + preview.ShowDialog(); + } + + void pnlEstimate_OnCalculateClicked(object sender, EventArgs e) + { + estimate.ScriptTime = InstrumentInfo.ScriptGenerator.EstimateRunTime(data.Runs, settings.Experiment, estimate); + } + + private void btnCreateNewTable_Click(object sender, RoutedEventArgs e) + { + data.Runs.Clear(); + } + + private void btnOpenExcel_Click(object sender, RoutedEventArgs e) + { + Microsoft.Win32.OpenFileDialog dlg = new Microsoft.Win32.OpenFileDialog(); + dlg.DefaultExt = ".xlsx"; + dlg.Filter = "Excel Files|*.xls;*.xlsx"; + dlg.Multiselect = false; + + if ((bool)dlg.ShowDialog()) + { + try + { + ExcelIO excel = new ExcelIO(); + excel.OpenExcelWorkBook(dlg.FileName); + List exps = excel.ExtractExperiments(); + data.Runs = new ObservableCollection(exps); + } + catch (Exception err) + { + MessageBox.Show("Could not open Excel spreadsheet", "SANS Script - Error", MessageBoxButton.OK, MessageBoxImage.Error); + } + } + } + + private void mnuExit_Click(object sender, RoutedEventArgs e) + { + Close(); + } + + private void mnuExportToExcel_Click(object sender, RoutedEventArgs e) + { + ExcelIO excel = new ExcelIO(); + excel.ExportDataToExcel(data.Runs); + } + + private void mnuSaveCsv_Click(object sender, RoutedEventArgs e) + { + Microsoft.Win32.SaveFileDialog dlg = new Microsoft.Win32.SaveFileDialog(); + dlg.DefaultExt = ".csv"; + dlg.Filter = "CSV File|*.csv"; + + if ((bool)dlg.ShowDialog()) + { + try + { + CsvExporter.Export(dlg.FileName, data.Runs); + } + catch (Exception err) + { + MessageBox.Show("Could not save as CSV", "SANS Script - Error", MessageBoxButton.OK, MessageBoxImage.Error); + } + } + } + + private void Window_Closed(object sender, EventArgs e) + { + if (instrument.IsLarmor) + { + Properties.Settings.Default.LastInstrument = "LARMOR"; + } + else if (instrument.IsLoq) + { + Properties.Settings.Default.LastInstrument = "LOQ"; + } + else if (instrument.IsSans2d) + { + Properties.Settings.Default.LastInstrument = "SANS2D"; + } + else if (instrument.IsZoom) + { + Properties.Settings.Default.LastInstrument = "ZOOM"; + } + + Properties.Settings.Default.Save(); + } + } +} diff --git a/Models/A2Setting.cs b/Models/A2Setting.cs new file mode 100644 index 0000000..3111874 --- /dev/null +++ b/Models/A2Setting.cs @@ -0,0 +1,24 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Collections.ObjectModel; +using System.ComponentModel; + +namespace LOQ_Script_Gui +{ + class A2Setting + { + public readonly static string XLarge = "XLarge = 20x18 mm"; + public readonly static string Large = "Large = 17 mm"; + public readonly static string Medium = "Medium = 12 mm"; + public readonly static string Small = "Small = 5/1 mm"; + public readonly static string Trans = "Trans = 1 mm"; + + public readonly static string Larmor = ""; + //public readonly static string LarmorLarge = "Large = 25, 16 mm"; + //public readonly static string LarmorMedium = "Medium = 20, 14 mm"; + //public readonly static string LarmorSmall = "Small = 15, 10 mm"; + } +} diff --git a/Models/A2Setting.py b/Models/A2Setting.py new file mode 100644 index 0000000..7a55dee --- /dev/null +++ b/Models/A2Setting.py @@ -0,0 +1,19 @@ +class A2Setting: + X_LARGE = "XLarge = 20x18 mm" + LARGE = "Large = 17 mm" + MEDIUM = "Medium = 12 mm" + SMALL = "Small = 5/1 mm" + TRANS = "Trans = 1 mm" + LARMOR = "" + LARMOR_LARGE = "Large = 25, 16 mm" + LARMOR_MEDIUM = "Medium = 20, 14 mm" + LARMOR_SMALL = "Small = 15, 10 mm" + +# Example usage +if __name__ == "__main__": + print(A2Setting.X_LARGE) + print(A2Setting.LARGE) + print(A2Setting.MEDIUM) + print(A2Setting.SMALL) + print(A2Setting.TRANS) + print(A2Setting.LARMOR) \ No newline at end of file diff --git a/Models/DataCollection.cs b/Models/DataCollection.cs new file mode 100644 index 0000000..72fb51b --- /dev/null +++ b/Models/DataCollection.cs @@ -0,0 +1,26 @@ +using System.Collections.ObjectModel; + +namespace LOQ_Script_Gui +{ + class DataCollection + { + public readonly static string Histogram = "Histogram"; + public readonly static string Events = "Events"; + public readonly static string SESANS = "SESANS"; + public readonly static string TRANSSANS_Events = "TRANSSANS_Events"; + public readonly static string TRANSSANS_Histogram = "TRANSSANS_Histogram"; + + static ObservableCollection choices = new ObservableCollection() + { + Histogram, TRANSSANS_Histogram, Events, TRANSSANS_Events, SESANS + }; + + public static ObservableCollection Choices + { + get + { + return choices; + } + } + } +} diff --git a/Models/DataCollection.py b/Models/DataCollection.py new file mode 100644 index 0000000..1d3ed8c --- /dev/null +++ b/Models/DataCollection.py @@ -0,0 +1,22 @@ +class DataCollection: + HISTOGRAM = "Histogram" + EVENTS = "Events" + SESANS = "SESANS" + TRANSSANS_EVENTS = "TRANSSANS_Events" + TRANSSANS_HISTOGRAM = "TRANSSANS_Histogram" + + choices = [ + HISTOGRAM, + TRANSSANS_HISTOGRAM, + EVENTS, + TRANSSANS_EVENTS, + SESANS + ] + + @staticmethod + def get_choices(): + return DataCollection.choices + +# Example usage +if __name__ == "__main__": + print(DataCollection.get_choices()) \ No newline at end of file diff --git a/Models/Estimation.cs b/Models/Estimation.cs new file mode 100644 index 0000000..459a634 --- /dev/null +++ b/Models/Estimation.cs @@ -0,0 +1,115 @@ +using System.ComponentModel; +using System; + +namespace LOQ_Script_Gui +{ + class Estimation : INotifyPropertyChanged, IDataErrorInfo + { + public event PropertyChangedEventHandler PropertyChanged; + + protected void OnPropertyChanged(string name) + { + PropertyChangedEventHandler handler = PropertyChanged; + if (handler != null) + { + handler(this, new PropertyChangedEventArgs(name)); + } + } + + private double countRate = 40; + public double CountRate + { + get + { + return countRate; + } + set + { + countRate = value; + OnPropertyChanged("CountRate"); + } + } + + private double moveTime = 120; + public double MoveTime + { + get + { + return moveTime; + } + set + { + moveTime = value; + OnPropertyChanged("MoveTime"); + } + } + + private string scriptTime; + public string ScriptTime + { + get + { + return scriptTime; + } + set + { + scriptTime = value; + OnPropertyChanged("ScriptTime"); + } + } + + private bool isValid; + public bool IsValid + { + get + { + return isValid; + } + set + { + isValid = value; + OnPropertyChanged("IsValid"); + } + } + + public string Error + { + get { return String.Empty; } + } + + public string this[string columnName] + { + get + { + string errorMessage = String.Empty; + + switch (columnName) + { + case "CountRate": + if (CountRate < 0) + { + errorMessage = "Count rate cannot be negative!"; + } + break; + case "MoveTime": + if (MoveTime < 0) + { + errorMessage = "Move time cannot be negative!"; + } + break; + } + + if (string.IsNullOrWhiteSpace(errorMessage)) + { + IsValid = true; + } + else + { + IsValid = false; + } + + return errorMessage; + } + } + } +} diff --git a/Models/Estimation.py b/Models/Estimation.py new file mode 100644 index 0000000..9d296b5 --- /dev/null +++ b/Models/Estimation.py @@ -0,0 +1,89 @@ +import tkinter as tk + + + + +class Estimation: + def __init__(self, count_rate=0, move_time=0, script_time = 0): + + self._count_rate = tk.DoubleVar(value=40) + self._move_time = tk.DoubleVar(value=120) + self._script_time = tk.DoubleVar(value=0) + + self._is_valid = tk.BooleanVar(value=True) + + + self.property_changed = [] + + self._count_rate.trace_add("write", self.validate) + self._move_time.trace_add("write", self.validate) + self._script_time.trace_add("write", self.validate) + + @property + def count_rate(self): + return self._count_rate.get() + + @count_rate.setter + def count_rate(self, value): + self._count_rate.set(value) + self.on_property_changed("count_rate") + + @property + def move_time(self): + return self._move_time.get() + + @move_time.setter + def move_time(self, value): + self._move_time.set(value) + self.on_property_changed("move_time") + + @property + def script_time(self): + return self._script_time.get() + + @script_time.setter + def script_time(self, value): + self._script_time.set(value) + self.on_property_changed("script_time") + + @property + def is_valid(self): + return self._is_valid.get() + + @is_valid.setter + def is_valid(self, value): + self._is_valid.set(value) + self.on_property_changed("is_valid") + + def on_property_changed(self, name): + for callback in self.property_changed: + callback(name) + + def validate(self, *args): + error_message = "" + if self.count_rate < 0: + error_message = "Count rate cannot be negative!" + elif self.move_time < 0: + error_message = "Move time cannot be negative!" + elif self.script_time < 0: + error_message = "Script time cannot be negative!" + if error_message: + self.is_valid = False + else: + self.is_valid = True + + return error_message + +# Example usage +if __name__ == "__main__": + def on_property_changed(name): + print(f"Property {name} changed") + + estimation = Estimation() + estimation.property_changed.append(on_property_changed) + + print(estimation.count_rate) + estimation.count_rate = -10 # This should trigger validation and set is_valid to False + print(estimation.is_valid) + estimation.count_rate = 50 # This should trigger validation and set is_valid to True + print(estimation.is_valid) \ No newline at end of file diff --git a/Models/Experiment.cs b/Models/Experiment.cs new file mode 100644 index 0000000..d88a6ba --- /dev/null +++ b/Models/Experiment.cs @@ -0,0 +1,290 @@ +using System.ComponentModel; + +namespace LOQ_Script_Gui +{ + class Experiment : INotifyPropertyChanged + { + public event PropertyChangedEventHandler PropertyChanged; + + protected void OnPropertyChanged(string name) + { + PropertyChangedEventHandler handler = PropertyChanged; + if (handler != null) + { + handler(this, new PropertyChangedEventArgs(name)); + } + } + + private string position; + public string Position + { + get + { + return position; + } + set + { + position = value; + OnPropertyChanged("Position"); + } + } + + private double trans; + public double Trans + { + get + { + return trans; + } + set + { + trans = value; + OnPropertyChanged("Trans"); + } + } + + private string transWait; + public string TransWait + { + get + { + return transWait; + } + set + { + transWait = value; + OnPropertyChanged("TransWait"); + } + } + + private double sans; + public double Sans + { + get + { + return sans; + } + set + { + sans = value; + OnPropertyChanged("Sans"); + } + } + + private string sansWait; + public string SansWait + { + get + { + return sansWait; + } + set + { + sansWait = value; + OnPropertyChanged("SansWait"); + } + } + + private string period; + public string Period + { + get + { + return period; + } + set + { + period = value; + OnPropertyChanged("Period"); + } + } + + private string background; + public string Background + { + get + { + return background; + } + set + { + background = value; + OnPropertyChanged("Background"); + } + } + + private string sample; + public string Sample + { + get + { + return sample; + } + set + { + sample = value; + OnPropertyChanged("Sample"); + } + } + + private string thickness; + public string Thickness + { + get + { + return thickness; + } + set + { + thickness = value; + OnPropertyChanged("Thickness"); + } + } + + private string temperature1; + public string Temperature1 + { + get + { + return temperature1; + } + set + { + temperature1 = value; + OnPropertyChanged("Temperature1"); + } + } + + private string temperature2; + public string Temperature2 + { + get + { + return temperature2; + } + set + { + temperature2 = value; + OnPropertyChanged("Temperature2"); + } + } + + private string field; + public string Field + { + get + { + return field; + } + set + { + field = value; + OnPropertyChanged("Field"); + } + } + + private string shearRate1; + public string ShearRate1 + { + get + { + return shearRate1; + } + set + { + shearRate1 = value; + OnPropertyChanged("ShearRate1"); + } + } + + private string shearRate2; + public string ShearRate2 + { + get + { + return shearRate2; + } + set + { + shearRate2 = value; + OnPropertyChanged("ShearRate2"); + } + } + + private string shearAngle1; + public string ShearAngle1 + { + get + { + return shearAngle1; + } + set + { + shearAngle1 = value; + OnPropertyChanged("ShearAngle1"); + } + } + + private string shearAngle2; + public string ShearAngle2 + { + get + { + return shearAngle2; + } + set + { + shearAngle2 = value; + OnPropertyChanged("ShearAngle2"); + } + } + + private string preCommand; + public string PreCommand + { + get + { + return preCommand; + } + set + { + preCommand = value; + OnPropertyChanged("PreCommand"); + } + } + + private string postCommand; + public string PostCommand + { + get + { + return postCommand; + } + set + { + postCommand = value; + OnPropertyChanged("PostCommand"); + } + } + + private string rbNumber; + public string RbNumber + { + get + { + return rbNumber; + } + set + { + rbNumber = value; + OnPropertyChanged("RbNumber"); + } + } + + public Experiment() + { + sans = 0; + trans = 0; + } + } +} diff --git a/Models/Experiment.py b/Models/Experiment.py new file mode 100644 index 0000000..8aaefa0 --- /dev/null +++ b/Models/Experiment.py @@ -0,0 +1,223 @@ +import tkinter as tk + +class Experiment: + + def __init__(self): + self._position = tk.StringVar(value='') + self._trans = tk.DoubleVar(value=0) + self._trans_wait = tk.StringVar(value='') + self._sans = tk.DoubleVar(value=0) + self._sans_wait = tk.StringVar(value='') + self._period = tk.StringVar(value='') + self._background = tk.StringVar(value='') + self._sample = tk.StringVar(value='') + self._thickness = tk.DoubleVar(value=0) + self._temperature1 = tk.StringVar(value='') + self._temperature2 = tk.StringVar(value='') + self._field = tk.StringVar(value='') + self._shear_rate1 = tk.StringVar(value='') + self._shear_rate2 = tk.StringVar(value='') + self._shear_angle1 = tk.StringVar(value='') + self._shear_angle2 = tk.StringVar(value='') + self._pre_command = tk.StringVar(value='') + self._post_command = tk.StringVar(value='') + self._rb_number = tk.StringVar(value='') + self.property_changed = [] + + self._position.trace_add("write", self.on_property_changed) + self._trans.trace_add("write", self.on_property_changed) + self._trans_wait.trace_add("write", self.on_property_changed) + self._sans.trace_add("write", self.on_property_changed) + self._sans_wait.trace_add("write", self.on_property_changed) + self._period.trace_add("write", self.on_property_changed) + self._background.trace_add("write", self.on_property_changed) + self._sample.trace_add("write", self.on_property_changed) + self._thickness.trace_add("write", self.on_property_changed) + self._temperature1.trace_add("write", self.on_property_changed) + self._temperature2.trace_add("write", self.on_property_changed) + self._field.trace_add("write", self.on_property_changed) + self._shear_rate1.trace_add("write", self.on_property_changed) + self._shear_rate2.trace_add("write", self.on_property_changed) + self._shear_angle1.trace_add("write", self.on_property_changed) + self._shear_angle2.trace_add("write", self.on_property_changed) + self._pre_command.trace_add("write", self.on_property_changed) + self._post_command.trace_add("write", self.on_property_changed) + self._rb_number.trace_add("write", self.on_property_changed) + + def on_property_changed(self, *args): + for callback in self.property_changed: + callback(args[0]) + + @property + def position(self): + return self._position.get() + + @position.setter + def position(self, value): + self._position.set(value) + + @property + def trans(self): + return self._trans.get() + + @trans.setter + def trans(self, value): + self._trans.set(value) + + @property + def trans_wait(self): + return self._trans_wait.get() + + @trans_wait.setter + def trans_wait(self, value): + self._trans_wait.set(value) + + @property + def sans(self): + return self._sans.get() + + @sans.setter + def sans(self, value): + self._sans.set(value) + + @property + def sans_wait(self): + return self._sans_wait.get() + + @sans_wait.setter + def sans_wait(self, value): + self._sans_wait.set(value) + + @property + def period(self): + return self._period.get() + + @period.setter + def period(self, value): + self._period.set(value) + + @property + def background(self): + return self._background.get() + + @background.setter + def background(self, value): + self._background.set(value) + + @property + def sample(self): + return self._sample.get() + + @sample.setter + def sample(self, value): + self._sample.set(value) + + @property + def thickness(self): + return self._thickness.get() + + @thickness.setter + def thickness(self, value): + self._thickness.set(value) + + @property + def temperature1(self): + return self._temperature1.get() + + @temperature1.setter + def temperature1(self, value): + self._temperature1.set(value) + + @property + def temperature2(self): + return self._temperature2.get() + + @temperature2.setter + def temperature2(self, value): + self._temperature2.set(value) + + @property + def field(self): + return self._field.get() + + @field.setter + def field(self, value): + self._field.set(value) + + @property + def shear_rate1(self): + return self._shear_rate1.get() + + @shear_rate1.setter + def shear_rate1(self, value): + self._shear_rate1.set(value) + + @property + def shear_rate2(self): + return self._shear_rate2.get() + + @shear_rate2.setter + def shear_rate2(self, value): + self._shear_rate2.set(value) + + @property + def shear_angle1(self): + return self._shear_angle1.get() + + @shear_angle1.setter + def shear_angle1(self, value): + self._shear_angle1.set(value) + + @property + def shear_angle2(self): + return self._shear_angle2.get() + + @shear_angle2.setter + def shear_angle2(self, value): + self._shear_angle2.set(value) + + @property + def pre_command(self): + return self._pre_command.get() + + @pre_command.setter + def pre_command(self, value): + self._pre_command.set(value) + + @property + def post_command(self): + return self._post_command.get() + + @post_command.setter + def post_command(self, value): + self._post_command.set(value) + + @property + def rb_number(self): + return self._rb_number.get() + + @rb_number.setter + def rb_number(self, value): + self._rb_number.set(value) + +# Example usage +if __name__ == "__main__": + def on_property_changed(name): + print(f"Property {name} changed") + + experiment = Experiment() + experiment.property_changed.append(on_property_changed) + + experiment.position = "New Position" + experiment.trans = 10.5 + experiment.sans = 20.0 + experiment.temperature1 = "25" + experiment.temperature2 = "30" + experiment.field = "1.5T" + experiment.shear_rate1 = "100" + experiment.shear_rate2 = "200" + experiment.shear_angle1 = "45" + experiment.shear_angle2 = "90" + experiment.pre_command = "Pre Command" + experiment.post_command = "Post Command" + experiment.rb_number = "RB12345" \ No newline at end of file diff --git a/Models/ExperimentSettings.cs b/Models/ExperimentSettings.cs new file mode 100644 index 0000000..dee32a1 --- /dev/null +++ b/Models/ExperimentSettings.cs @@ -0,0 +1,292 @@ +using System; +using System.ComponentModel; + +namespace LOQ_Script_Gui +{ + class ExperimentSettings : INotifyPropertyChanged, IDataErrorInfo + { + public event PropertyChangedEventHandler PropertyChanged; + + protected void OnPropertyChanged(string name) + { + PropertyChangedEventHandler handler = PropertyChanged; + if (handler != null) + { + handler(this, new PropertyChangedEventArgs(name)); + } + } + + //private string order = RunOrder.TransFirst; + private string order = Properties.Settings.Default.RunOrder; + + public string Order + { + get + { + return order; + } + set + { + order = value; + OnPropertyChanged("Order"); + Properties.Settings.Default.RunOrder = order; + Properties.Settings.Default.Save(); + } + } + + //private int numSans = 1; + private int numSans = Convert.ToInt16(Properties.Settings.Default.NumSans); + public int NumSans + { + get + { + return numSans; + } + set + { + numSans = value; + OnPropertyChanged("NumSans"); + Properties.Settings.Default.NumSans = Convert.ToString(numSans); + Properties.Settings.Default.Save(); + } + } + + //private int numTrans = 1; + private int numTrans = Convert.ToInt16(Properties.Settings.Default.NumTrans); + public int NumTrans + { + get + { + return numTrans; + } + set + { + numTrans = value; + OnPropertyChanged("NumTrans"); + Properties.Settings.Default.NumTrans = Convert.ToString(numTrans); + Properties.Settings.Default.Save(); + } + } + + //private bool loopPerRun; + private bool loopPerRun = Convert.ToBoolean(Properties.Settings.Default.LoopPerRun); + + public bool LoopPerRun + { + get + { + return loopPerRun; + } + set + { + loopPerRun = value; + OnPropertyChanged("LoopPerRun"); + Properties.Settings.Default.LoopPerRun = Convert.ToString(LoopPerRun); + Properties.Settings.Default.Save(); + } + } + + //private string a2SettingSans = A2Setting.Large; + private string a2SettingSans = Properties.Settings.Default.A2SettingSans; + public string A2SettingSans + { + get + { + return a2SettingSans; + } + set + { + a2SettingSans = value; + OnPropertyChanged("A2SettingSans"); + Properties.Settings.Default.A2SettingSans = a2SettingSans; + Properties.Settings.Default.Save(); + } + } + + //private string a2SettingTrans = A2Setting.Large; + private string a2SettingTrans = Properties.Settings.Default.A2SettingTrans; + public string A2SettingTrans + { + get + { + return a2SettingTrans; + } + set + { + a2SettingTrans = value; + OnPropertyChanged("A2SettingTrans"); + Properties.Settings.Default.A2SettingTrans = a2SettingTrans; + Properties.Settings.Default.Save(); + } + } + + //private string sampleGeometry = Geometry.FlatPlate; + private string sampleGeometry = Properties.Settings.Default.Geometry; + public string SampleGeometry + { + get + { + return sampleGeometry; + } + set + { + sampleGeometry = value; + + if (sampleGeometry == Geometry.Disc) + { + SampleWidthEnabled = false; + } + else + { + SampleWidthEnabled = true; + } + + OnPropertyChanged("SampleGeometry"); + Properties.Settings.Default.Geometry = sampleGeometry; + Properties.Settings.Default.Save(); + } + } + + //private double sampleHeight = 11; + private double sampleHeight = Convert.ToDouble(Properties.Settings.Default.SampleHeight); + public double SampleHeight + { + get + { + return sampleHeight; + } + set + { + sampleHeight = value; + if (!sampleWidthEnabled) + { + // Width takes the same value as height + SampleWidth = value; + } + + OnPropertyChanged("SampleHeight"); + Properties.Settings.Default.SampleHeight = Convert.ToString(sampleHeight); + Properties.Settings.Default.Save(); + } + } + + //private double sampleWidth = 11; + private double sampleWidth = Convert.ToDouble(Properties.Settings.Default.SampleWidth); + public double SampleWidth + { + get + { + return sampleWidth; + } + set + { + sampleWidth = value; + OnPropertyChanged("SampleWidth"); + Properties.Settings.Default.SampleWidth = Convert.ToString(sampleWidth); + Properties.Settings.Default.Save(); + } + } + + private bool sampleWidthEnabled = true; + public bool SampleWidthEnabled + { + get + { + return sampleWidthEnabled; + } + set + { + sampleWidthEnabled = value; + if (!value) + { + // Width takes the same value as height + SampleWidth = SampleHeight; + } + + OnPropertyChanged("SampleWidthEnabled"); + } + } + + //private string collectionMode = DataCollection.Events; + private string collectionMode = Properties.Settings.Default.CollectionMode; + public string CollectionMode + { + get + { + return collectionMode; + } + set + { + collectionMode = value; + OnPropertyChanged("CollectionMode"); + Properties.Settings.Default.CollectionMode = collectionMode; + Properties.Settings.Default.Save(); + } + } + + private bool isValid = true; + public bool IsValid + { + get + { + return isValid; + } + set + { + isValid = value; + OnPropertyChanged("IsValid"); + } + } + + public string Error + { + get { return String.Empty; } + } + + public string this[string columnName] + { + get + { + string errorMessage = String.Empty; + + switch (columnName) + { + case "NumSans": + if (NumSans < 0) + { + errorMessage = "Number of SANS runs cannot be negative!"; + } + break; + case "NumTrans": + if (NumTrans < 0) + { + errorMessage = "Number of TRANS runs cannot be negative!"; + } + break; + case "SampleHeight": + if (SampleHeight <= 0 || SampleHeight > 20) + { + errorMessage = "Sample height must be between 0 and 20"; + } + break; + case "SampleWidth": + if (SampleWidth <= 0 || SampleWidth > 20) + { + errorMessage = "Sample width must be between 0 and 20"; + } + break; + } + + if (string.IsNullOrWhiteSpace(errorMessage)) + { + IsValid = true; + } + else + { + IsValid = false; + } + return errorMessage; + } + } + } +} diff --git a/Models/ExperimentSettings.py b/Models/ExperimentSettings.py new file mode 100644 index 0000000..c448c54 --- /dev/null +++ b/Models/ExperimentSettings.py @@ -0,0 +1,187 @@ +import tkinter as tk +from configparser import ConfigParser + +class ExperimentSettings: + def __init__(self, config_file='settings.ini'): + self.config = ConfigParser() + self.config.read(config_file) + self.config_file = config_file + + self._order = tk.StringVar(value=self.config['DEFAULT'].get('RunOrder', 'TransFirst')) + self._num_sans = tk.IntVar(value=self.config['DEFAULT'].getint('NumSans', 1)) + self._num_trans = tk.IntVar(value=self.config['DEFAULT'].getint('NumTrans', 1)) + self._loop_per_run = tk.BooleanVar(value=self.config['DEFAULT'].getboolean('LoopPerRun', False)) + self._a2_setting_sans = tk.StringVar(value=self.config['DEFAULT'].get('A2SettingSans', 'Large')) + self._a2_setting_trans = tk.StringVar(value=self.config['DEFAULT'].get('A2SettingTrans', 'Large')) + self._sample_geometry = tk.StringVar(value=self.config['DEFAULT'].get('Geometry', 'Flat Plate')) + self._sample_height = tk.DoubleVar(value=self.config['DEFAULT'].getfloat('SampleHeight', 11)) + self._sample_width = tk.DoubleVar(value=self.config['DEFAULT'].getfloat('SampleWidth', 11)) + self._sample_width_enabled = tk.BooleanVar(value=True) + self._collection_mode = tk.StringVar(value=self.config['DEFAULT'].get('CollectionMode', 'Events')) + self._is_valid = tk.BooleanVar(value=True) + self.property_changed = [] + + self._order.trace_add("write", self.on_property_changed) + self._num_sans.trace_add("write", self.on_property_changed) + self._num_trans.trace_add("write", self.on_property_changed) + self._loop_per_run.trace_add("write", self.on_property_changed) + self._a2_setting_sans.trace_add("write", self.on_property_changed) + self._a2_setting_trans.trace_add("write", self.on_property_changed) + self._sample_geometry.trace_add("write", self.on_property_changed) + self._sample_height.trace_add("write", self.on_property_changed) + self._sample_width.trace_add("write", self.on_property_changed) + self._collection_mode.trace_add("write", self.on_property_changed) + + def on_property_changed(self, *args): + for callback in self.property_changed: + callback(args[0]) + self.save() + + def save(self): + self.config['DEFAULT']['RunOrder'] = self._order.get() + self.config['DEFAULT']['NumSans'] = str(self._num_sans.get()) + self.config['DEFAULT']['NumTrans'] = str(self._num_trans.get()) + self.config['DEFAULT']['LoopPerRun'] = str(self._loop_per_run.get()) + self.config['DEFAULT']['A2SettingSans'] = self._a2_setting_sans.get() + self.config['DEFAULT']['A2SettingTrans'] = self._a2_setting_trans.get() + self.config['DEFAULT']['Geometry'] = self._sample_geometry.get() + self.config['DEFAULT']['SampleHeight'] = str(self._sample_height.get()) + self.config['DEFAULT']['SampleWidth'] = str(self._sample_width.get()) + self.config['DEFAULT']['CollectionMode'] = self._collection_mode.get() + with open(self.config_file, 'w') as configfile: + self.config.write(configfile) + + @property + def order(self): + return self._order.get() + + @order.setter + def order(self, value): + self._order.set(value) + + @property + def num_sans(self): + return self._num_sans.get() + + @num_sans.setter + def num_sans(self, value): + self._num_sans.set(value) + + @property + def num_trans(self): + return self._num_trans.get() + + @num_trans.setter + def num_trans(self, value): + self._num_trans.set(value) + + @property + def loop_per_run(self): + return self._loop_per_run.get() + + @loop_per_run.setter + def loop_per_run(self, value): + self._loop_per_run.set(value) + + @property + def a2_setting_sans(self): + return self._a2_setting_sans.get() + + @a2_setting_sans.setter + def a2_setting_sans(self, value): + self._a2_setting_sans.set(value) + + @property + def a2_setting_trans(self): + return self._a2_setting_trans.get() + + @a2_setting_trans.setter + def a2_setting_trans(self, value): + self._a2_setting_trans.set(value) + + @property + def sample_geometry(self): + return self._sample_geometry.get() + + @sample_geometry.setter + def sample_geometry(self, value): + self._sample_geometry.set(value) + self.sample_width_enabled = value != 'Disc' + + @property + def sample_height(self): + return self._sample_height.get() + + @sample_height.setter + def sample_height(self, value): + self._sample_height.set(value) + if not self.sample_width_enabled: + self.sample_width = value + + @property + def sample_width(self): + return self._sample_width.get() + + @sample_width.setter + def sample_width(self, value): + self._sample_width.set(value) + + @property + def sample_width_enabled(self): + return self._sample_width_enabled.get() + + @sample_width_enabled.setter + def sample_width_enabled(self, value): + self._sample_width_enabled.set(value) + if not value: + self.sample_width = self.sample_height + + @property + def collection_mode(self): + return self._collection_mode.get() + + @collection_mode.setter + def collection_mode(self, value): + self._collection_mode.set(value) + + @property + def is_valid(self): + return self._is_valid.get() + + @is_valid.setter + def is_valid(self, value): + self._is_valid.set(value) + + def validate(self): + error_message = "" + if self.num_sans < 0: + error_message = "Number of SANS runs cannot be negative!" + elif self.num_trans < 0: + error_message = "Number of TRANS runs cannot be negative!" + elif self.sample_height <= 0 or self.sample_height > 20: + error_message = "Sample height must be between 0 and 20" + elif self.sample_width <= 0 or self.sample_width > 20: + error_message = "Sample width must be between 0 and 20" + + if error_message: + self.is_valid = False + else: + self.is_valid = True + + return error_message + +# Example usage +if __name__ == "__main__": + def on_property_changed(name): + print(f"Property {name} changed") + + settings = ExperimentSettings() + settings.property_changed.append(on_property_changed) + + settings.order = "SansFirst" + settings.num_sans = 2 + settings.num_trans = 3 + settings.sample_height = 15 + settings.sample_width = 10 + settings.collection_mode = "Histogram" + print(settings.validate()) \ No newline at end of file diff --git a/Models/Geometry.cs b/Models/Geometry.cs new file mode 100644 index 0000000..d20abc8 --- /dev/null +++ b/Models/Geometry.cs @@ -0,0 +1,25 @@ +using System.Collections.ObjectModel; + +namespace LOQ_Script_Gui +{ + class Geometry + { + public readonly static string Disc = "Disc"; + public readonly static string Cylindrical = "Cylindrical"; + public readonly static string FlatPlate = "Flat Plate"; + public readonly static string SingleCrystal = "Single Crystal"; + + static ObservableCollection choices = new ObservableCollection() + { + Disc, Cylindrical, FlatPlate, SingleCrystal + }; + + public static ObservableCollection Choices + { + get + { + return choices; + } + } + } +} diff --git a/Models/Geometry.py b/Models/Geometry.py new file mode 100644 index 0000000..6e91165 --- /dev/null +++ b/Models/Geometry.py @@ -0,0 +1,20 @@ +class Geometry: + DISC = "Disc" + CYLINDRICAL = "Cylindrical" + FLAT_PLATE = "Flat Plate" + SINGLE_CRYSTAL = "Single Crystal" + + choices = [ + DISC, + CYLINDRICAL, + FLAT_PLATE, + SINGLE_CRYSTAL + ] + + @staticmethod + def get_choices(): + return Geometry.choices + +# Example usage +if __name__ == "__main__": + print(Geometry.get_choices()) \ No newline at end of file diff --git a/Models/InstrumentInfo.cs b/Models/InstrumentInfo.cs new file mode 100644 index 0000000..00ce2fc --- /dev/null +++ b/Models/InstrumentInfo.cs @@ -0,0 +1,98 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using LOQ_Script_Gui.Scripting; +using System.Collections.ObjectModel; + +namespace LOQ_Script_Gui +{ + /// + /// Contains the information needed to set the GUI up for the specified instrument. + /// + class InstrumentInfo : INotifyPropertyChanged + { + public event PropertyChangedEventHandler PropertyChanged; + + protected void OnPropertyChanged(string name) + { + PropertyChangedEventHandler handler = PropertyChanged; + if (handler != null) + { + handler(this, new PropertyChangedEventArgs(name)); + } + } + + public static event EventHandler StaticPropertyChanged = delegate { }; + + protected static void OnStaticPropertyChanged(string propertyName) + { + if (StaticPropertyChanged != null) + StaticPropertyChanged(null, new PropertyChangedEventArgs(propertyName)); + } + + static string a2Label = "A2 Setting:"; + public static string A2Label + { + get + { + return a2Label; + } + set + { + a2Label = value; + OnStaticPropertyChanged("A2Label"); + } + } + + // Provides the combo box options + // These values can change depending on the instrument + static ObservableCollection a2Choices = new ObservableCollection() + { + A2Setting.Large, A2Setting.Medium, A2Setting.Small + }; + + public static ObservableCollection A2Choices + { + get + { + return a2Choices; + } + + set + { + a2Choices = value; + OnStaticPropertyChanged("A2Choices"); + } + } + + static bool a2TransEnabled = true; + public static bool A2TransEnabled + { + get + { + return a2TransEnabled; + } + set + { + a2TransEnabled = value; + OnStaticPropertyChanged("A2TransEnabled"); + } + } + + static bool collectionModeEnabled = false; + public static bool CollectionModeEnabled + { + get + { + return collectionModeEnabled; + } + set + { + collectionModeEnabled = value; + OnStaticPropertyChanged("CollectionModeEnabled"); + } + } + + public static IScriptGenerator ScriptGenerator { get; set; } + } +} diff --git a/Models/InstrumentInfo.py b/Models/InstrumentInfo.py new file mode 100644 index 0000000..72598aa --- /dev/null +++ b/Models/InstrumentInfo.py @@ -0,0 +1,103 @@ +import tkinter as tk +from tkinter import StringVar, BooleanVar +from typing import List + +class InstrumentInfo: + property_changed = [] + static_property_changed = [] + script_generator = None + a2_label = "" + a2_choices = [] + a2_trans_enabled = False + collection_mode_enabled = False + + def __init__(self): + self._a2_label = StringVar(value="A2 Setting:") + self._a2_choices = tk.Variable(value=["Large", "Medium", "Small"]) + self._a2_trans_enabled = BooleanVar(value=True) + self._collection_mode_enabled = BooleanVar(value=False) + self._script_generator = None + + self._a2_label.trace_add("write", self.on_property_changed) + self._a2_choices.trace_add("write", self.on_property_changed) + self._a2_trans_enabled.trace_add("write", self.on_property_changed) + self._collection_mode_enabled.trace_add("write", self.on_property_changed) + + def on_property_changed(self, *args): + for callback in self.property_changed: + callback(args[0]) + + @staticmethod + def on_static_property_changed(property_name): + for callback in InstrumentInfo.static_property_changed: + callback(property_name) + + @property + def a2_label(self): + return self._a2_label.get() + + @a2_label.setter + def a2_label(self, value): + self._a2_label.set(value) + self.on_static_property_changed("a2_label") + + @property + def a2_choices(self): + return self._a2_choices.get() + + @a2_choices.setter + def a2_choices(self, value: List[str]): + self._a2_choices.set(value) + self.on_static_property_changed("a2_choices") + + @property + def a2_trans_enabled(self): + return self._a2_trans_enabled.get() + + @a2_trans_enabled.setter + def a2_trans_enabled(self, value): + self._a2_trans_enabled.set(value) + self.on_static_property_changed("a2_trans_enabled") + + @property + def collection_mode_enabled(self): + return self._collection_mode_enabled.get() + + @collection_mode_enabled.setter + def collection_mode_enabled(self, value): + self._collection_mode_enabled.set(value) + self.on_static_property_changed("collection_mode_enabled") + + @property + def script_generator(self): + return self._script_generator + + @script_generator.setter + def script_generator(self, value): + self._script_generator = value + + class ScriptGenerator: + @staticmethod + def generate_script(runs, experiment, scriptname): + return f"# Script for {scriptname}\n# Runs: {runs}\n# Experiment: {experiment}" + + @staticmethod + def estimate_run_time(runs, experiment, estimate): + return len(runs) * 10 # Dummy implementation + +# Example usage +if __name__ == "__main__": + def on_property_changed(name): + print(f"Property {name} changed") + + def on_static_property_changed(name): + print(f"Static property {name} changed") + + instrument_info = InstrumentInfo() + instrument_info.property_changed.append(on_property_changed) + InstrumentInfo.static_property_changed.append(on_static_property_changed) + + instrument_info.a2_label = "New A2 Setting:" + instrument_info.a2_choices = ["XLarge", "Large", "Medium", "Small"] + instrument_info.a2_trans_enabled = False + instrument_info.collection_mode_enabled = True \ No newline at end of file diff --git a/Models/RunOrder.cs b/Models/RunOrder.cs new file mode 100644 index 0000000..8aba130 --- /dev/null +++ b/Models/RunOrder.cs @@ -0,0 +1,25 @@ +using System.Collections.ObjectModel; + +namespace LOQ_Script_Gui +{ + class RunOrder + { + public readonly static string AllTrans = "All TRANS first"; + public readonly static string TransFirst = "Alternate - TRANS first"; + public readonly static string AllSans = "All SANS first"; + public readonly static string SansFirst = "Alternate - SANS first"; + + static ObservableCollection choices = new ObservableCollection() + { + AllTrans, TransFirst, AllSans, SansFirst + }; + + public static ObservableCollection Choices + { + get + { + return choices; + } + } + } +} diff --git a/Models/RunOrder.py b/Models/RunOrder.py new file mode 100644 index 0000000..73e5815 --- /dev/null +++ b/Models/RunOrder.py @@ -0,0 +1,20 @@ +class RunOrder: + ALL_TRANS = "All TRANS first" + TRANS_FIRST = "Alternate - TRANS first" + ALL_SANS = "All SANS first" + SANS_FIRST = "Alternate - SANS first" + + choices = [ + ALL_TRANS, + TRANS_FIRST, + ALL_SANS, + SANS_FIRST + ] + + @staticmethod + def get_choices(): + return RunOrder.choices + +# Example usage +if __name__ == "__main__": + print(RunOrder.get_choices()) \ No newline at end of file diff --git a/Models/WaitForUnits.cs b/Models/WaitForUnits.cs new file mode 100644 index 0000000..0484abf --- /dev/null +++ b/Models/WaitForUnits.cs @@ -0,0 +1,25 @@ +using System.Collections.ObjectModel; + +namespace LOQ_Script_Gui +{ + class WaitForUnits + { + public readonly static string MicroAmps = "Microamp-hours"; + public readonly static string Minutes = "Minutes"; + public readonly static string Seconds = "Seconds"; + public readonly static string Frames = "Frames"; + + static ObservableCollection choices = new ObservableCollection() + { + MicroAmps, Minutes, Seconds, Frames + }; + + public static ObservableCollection Choices + { + get + { + return choices; + } + } + } +} diff --git a/Models/WaitForUnits.py b/Models/WaitForUnits.py new file mode 100644 index 0000000..0df9622 --- /dev/null +++ b/Models/WaitForUnits.py @@ -0,0 +1,20 @@ +class WaitForUnits: + MICROAMPS = "Microamp-hours" + MINUTES = "Minutes" + SECONDS = "Seconds" + FRAMES = "Frames" + + choices = [ + MICROAMPS, + MINUTES, + SECONDS, + FRAMES + ] + + @staticmethod + def get_choices(): + return WaitForUnits.choices + +# Example usage +if __name__ == "__main__": + print(WaitForUnits.get_choices()) \ No newline at end of file diff --git a/Properties/AssemblyInfo.cs b/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..1f2339a --- /dev/null +++ b/Properties/AssemblyInfo.cs @@ -0,0 +1,55 @@ +using System.Reflection; +using System.Resources; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Windows; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("LOQ_Script_Gui")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("LOQ_Script_Gui")] +[assembly: AssemblyCopyright("Copyright © 2015")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +//In order to begin building localizable applications, set +//CultureYouAreCodingWith in your .csproj file +//inside a . For example, if you are using US english +//in your source files, set the to en-US. Then uncomment +//the NeutralResourceLanguage attribute below. Update the "en-US" in +//the line below to match the UICulture setting in the project file. + +//[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)] + + +[assembly: ThemeInfo( + ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located + //(used if a resource is not found in the page, + // or application resource dictionaries) + ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located + //(used if a resource is not found in the page, + // app, or any theme specific resource dictionaries) +)] + + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/Properties/AssemblyInfo.py b/Properties/AssemblyInfo.py new file mode 100644 index 0000000..223c844 --- /dev/null +++ b/Properties/AssemblyInfo.py @@ -0,0 +1,23 @@ +from setuptools import setup, find_packages + +setup( + name="LOQ_Script_Gui", + version="1.0.0", + description="", + author="", + author_email="", + url="", + packages=find_packages(), + include_package_data=True, + install_requires=[ + # Add your dependencies here + ], + classifiers=[ + "Programming Language :: Python :: 3", + "License :: OSI Approved :: MIT License", + "Operating System :: OS Independent", + ], + python_requires='>=3.6', +) + +# Additional metadata for localization and resource management can be added as needed. \ No newline at end of file diff --git a/Properties/Resources.Designer.cs b/Properties/Resources.Designer.cs new file mode 100644 index 0000000..2b67d17 --- /dev/null +++ b/Properties/Resources.Designer.cs @@ -0,0 +1,63 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace LOQ_Script_Gui.Properties { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Resources { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Resources() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("LOQ_Script_Gui.Properties.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + } +} diff --git a/Properties/Resources.Designer.py b/Properties/Resources.Designer.py new file mode 100644 index 0000000..d4614f7 --- /dev/null +++ b/Properties/Resources.Designer.py @@ -0,0 +1,31 @@ +import gettext +import os + +class Resources: + def __init__(self): + self.resource_man = None + self.resource_culture = None + + @property + def resource_manager(self): + if self.resource_man is None: + locale_dir = os.path.join(os.path.dirname(__file__), 'locale') + self.resource_man = gettext.translation('resources', localedir=locale_dir, languages=[self.resource_culture]) + return self.resource_man + + @property + def culture(self): + return self.resource_culture + + @culture.setter + def culture(self, value): + self.resource_culture = value + self.resource_man = None # Reset the resource manager to reload with the new culture + +# Example usage +resources = Resources() +resources.culture = 'en_US' # Set the desired culture +_ = resources.resource_manager.gettext + +# Example of using the localized string +print(_("Hello, World!")) \ No newline at end of file diff --git a/Properties/Resources.ini b/Properties/Resources.ini new file mode 100644 index 0000000..0d87d37 --- /dev/null +++ b/Properties/Resources.ini @@ -0,0 +1,16 @@ +#using configparser +[resheader] +resmimetype = text/microsoft-resx +version = 2.0 +reader = System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 +writer = System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + +[data] +Name1 = this is my long string +Color1 = Blue +Bitmap1 = [base64 mime encoded serialized .NET Framework object] +Icon1 = [base64 mime encoded string representing a byte array form of the .NET Framework object] + +[metadata] +Name1.comment = this is a comment +Icon1.comment = This is a comment \ No newline at end of file diff --git a/Properties/Resources.resx b/Properties/Resources.resx new file mode 100644 index 0000000..af7dbeb --- /dev/null +++ b/Properties/Resources.resx @@ -0,0 +1,117 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/Properties/Settings.Designer.cs b/Properties/Settings.Designer.cs new file mode 100644 index 0000000..5f01f51 --- /dev/null +++ b/Properties/Settings.Designer.cs @@ -0,0 +1,158 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace LOQ_Script_Gui.Properties { + + + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "17.0.3.0")] + internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { + + private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); + + public static Settings Default { + get { + return defaultInstance; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("LARMOR")] + public string LastInstrument { + get { + return ((string)(this["LastInstrument"])); + } + set { + this["LastInstrument"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("11")] + public string SampleHeight { + get { + return ((string)(this["SampleHeight"])); + } + set { + this["SampleHeight"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("11")] + public string SampleWidth { + get { + return ((string)(this["SampleWidth"])); + } + set { + this["SampleWidth"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("Flat Plate")] + public string Geometry { + get { + return ((string)(this["Geometry"])); + } + set { + this["Geometry"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("Alternate - TRANS first")] + public string RunOrder { + get { + return ((string)(this["RunOrder"])); + } + set { + this["RunOrder"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("1")] + public string NumTrans { + get { + return ((string)(this["NumTrans"])); + } + set { + this["NumTrans"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("1")] + public string NumSans { + get { + return ((string)(this["NumSans"])); + } + set { + this["NumSans"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("False")] + public string LoopPerRun { + get { + return ((string)(this["LoopPerRun"])); + } + set { + this["LoopPerRun"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("Large = 25, 16 mm")] + public string A2SettingSans { + get { + return ((string)(this["A2SettingSans"])); + } + set { + this["A2SettingSans"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("Large = 25, 16 mm")] + public string A2SettingTrans { + get { + return ((string)(this["A2SettingTrans"])); + } + set { + this["A2SettingTrans"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("Events")] + public string CollectionMode { + get { + return ((string)(this["CollectionMode"])); + } + set { + this["CollectionMode"] = value; + } + } + } +} diff --git a/Properties/Settings.ini b/Properties/Settings.ini new file mode 100644 index 0000000..76b9093 --- /dev/null +++ b/Properties/Settings.ini @@ -0,0 +1,13 @@ +[DEFAULT] +lastinstrument = LOQ +sampleheight = '11' +samplewidth = '11' +geometry = 'Flat Plate' +runorder = 'Alternate - TRANS first' +numtrans = '1' +numsans = '1' +loopperrun = 'False' +a2settingsans = +a2settingtrans = +collectionmode = 'Events' + diff --git a/Properties/Settings.py b/Properties/Settings.py new file mode 100644 index 0000000..6d9f60f --- /dev/null +++ b/Properties/Settings.py @@ -0,0 +1,119 @@ +import configparser +import os + +class Settings: + def __init__(self, filename='Settings.ini'): + self.dict_settings = configparser.ConfigParser() + directory=os.path.dirname(os.path.abspath(__file__)) + self.setting_ini_path = os.path.join(directory,filename) + + self.dict_settings.read(self.setting_ini_path) + + @property + def last_instrument(self): + return self.dict_settings.get('DEFAULT','LastInstrument') + + @last_instrument.setter + def last_instrument(self, value): + self.dict_settings.set('DEFAULT','LastInstrument', str(value)) + self.save() + + @property + def sample_height(self): + return self.dict_settings.get('DEFAULT','SampleHeight') + + @sample_height.setter + def sample_height(self, value): + self.dict_settings.set('DEFAULT','SampleHeight', str(value)) + self.save() + + @property + def sample_width(self): + return self.dict_settings.get('DEFAULT','SampleWidth') + + @sample_width.setter + def sample_width(self, value): + self.dict_settings.set('DEFAULT','SampleWidth', str(value)) + self.save() + + @property + def geometry(self): + return self.dict_settings.get('DEFAULT','Geometry') + + @geometry.setter + def geometry(self, value): + self.dict_settings.set('DEFAULT','Geometry', str(value)) + self.save() + + @property + def run_order(self): + return self.dict_settings.get('DEFAULT','RunOrder') + + @run_order.setter + def run_order(self, value): + self.dict_settings.set('DEFAULT','RunOrder', str(value)) + self.save() + + @property + def num_trans(self): + return self.dict_settings.get('DEFAULT','NumTrans') + + @num_trans.setter + def num_trans(self, value): + self.dict_settings.set('DEFAULT','NumTrans', str(value)) + self.save() + + @property + def num_sans(self): + return self.dict_settings.get('DEFAULT','NumSans') + + @num_sans.setter + def num_sans(self, value): + self.dict_settings.set('DEFAULT','NumSans', str(value)) + self.save() + + @property + def loop_per_run(self): + return self.dict_settings.get('DEFAULT','LoopPerRun') + + @loop_per_run.setter + def loop_per_run(self, value): + self.dict_settings.set('DEFAULT','LoopPerRun', str(value)) + self.save() + + @property + def a2_setting_sans(self): + return self.dict_settings.get('DEFAULT','A2SettingSans') + + @a2_setting_sans.setter + def a2_setting_sans(self, value): + self.dict_settings.set('DEFAULT','A2SettingSans', str(value)) + self.save() + + @property + def a2_setting_trans(self): + return self.dict_settings.get('DEFAULT','A2SettingTrans') + + @a2_setting_trans.setter + def a2_setting_trans(self, value): + self.dict_settings.set('DEFAULT','A2SettingTrans', str(value)) + self.save() + + @property + def collection_mode(self): + return self.dict_settings.get('DEFAULT','CollectionMode') + + @collection_mode.setter + def collection_mode(self, value): + self.dict_settings.set('DEFAULT','CollectionMode', str(value)) + self.save() + + def save(self): + with open(self.setting_ini_path, 'w') as configfile: + self.dict_settings.write(configfile) + +# Example usage +settings = Settings() +print(settings.last_instrument) +settings.last_instrument = 'LOQ' +print(settings.last_instrument) \ No newline at end of file diff --git a/Properties/Settings.settings b/Properties/Settings.settings new file mode 100644 index 0000000..27069e6 --- /dev/null +++ b/Properties/Settings.settings @@ -0,0 +1,39 @@ + + + + + + LARMOR + + + 11 + + + 11 + + + Flat Plate + + + Alternate - TRANS first + + + 1 + + + 1 + + + False + + + Large = 25, 16 mm + + + Large = 25, 16 mm + + + Events + + + \ No newline at end of file diff --git a/SANS_Icon.ico b/SANS_Icon.ico new file mode 100644 index 0000000000000000000000000000000000000000..514a7d8425efc1d81ec678aafd00095f5cbc87dc GIT binary patch literal 59390 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 Y`E%t*c(~Jru`u5oWi6#@1 + + + + Debug + AnyCPU + {5DBFF2C3-05A0-4B4B-9080-8224D86A7B40} + WinExe + Properties + LOQ_Script_Gui + SANS_Script_Gui_v4 + v4.8 + 512 + {60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + 4 + false + + publish\ + true + Disk + false + Foreground + 7 + Days + false + false + true + 0 + 1.0.0.%2a + false + true + + + x64 + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + false + + + x64 + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + SANS_Icon.ico + + + + True + + + + + + + + + + + 4.0 + + + + + + + + MSBuild:Compile + Designer + + + + + + PreviewDialog.xaml + + + + + + + + EstimationPanel.xaml + + + + + + + + + SettingsPanel.xaml + + + + + + + Designer + MSBuild:Compile + + + Designer + MSBuild:Compile + + + MSBuild:Compile + Designer + + + App.xaml + Code + + + MainWindow.xaml + Code + + + Designer + MSBuild:Compile + + + + + Code + + + True + True + Resources.resx + + + True + Settings.settings + True + + + ResXFileCodeGenerator + Resources.Designer.cs + + + SettingsSingleFileGenerator + Settings.Designer.cs + + + + + + + + + False + Microsoft .NET Framework 4.5 %28x86 and x64%29 + true + + + False + .NET Framework 3.5 SP1 + false + + + + + + + + + + + \ No newline at end of file diff --git a/SANS_Script_GUI_v4.csproj.py b/SANS_Script_GUI_v4.csproj.py new file mode 100644 index 0000000..10d6092 --- /dev/null +++ b/SANS_Script_GUI_v4.csproj.py @@ -0,0 +1,175 @@ +import xml.etree.ElementTree as ET + +root = ET.Element("Project", ToolsVersion="12.0", DefaultTargets="Build", xmlns="http://schemas.microsoft.com/developer/msbuild/2003") + +ET.SubElement(root, "Import", Project="$(MSBuildExtensionsPath)\\$(MSBuildToolsVersion)\\Microsoft.Common.props", Condition="Exists('$(MSBuildExtensionsPath)\\$(MSBuildToolsVersion)\\Microsoft.Common.props')") + +property_group = ET.SubElement(root, "PropertyGroup") +ET.SubElement(property_group, "Configuration", Condition=" '$(Configuration)' == '' ").text = "Debug" +ET.SubElement(property_group, "Platform", Condition=" '$(Platform)' == '' ").text = "AnyCPU" +ET.SubElement(property_group, "ProjectGuid").text = "{5DBFF2C3-05A0-4B4B-9080-8224D86A7B40}" +ET.SubElement(property_group, "OutputType").text = "WinExe" +ET.SubElement(property_group, "AppDesignerFolder").text = "Properties" +ET.SubElement(property_group, "RootNamespace").text = "LOQ_Script_Gui" +ET.SubElement(property_group, "AssemblyName").text = "SANS_Script_Gui_v4" +ET.SubElement(property_group, "TargetFrameworkVersion").text = "v4.8" +ET.SubElement(property_group, "FileAlignment").text = "512" +ET.SubElement(property_group, "ProjectTypeGuids").text = "{60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}" +ET.SubElement(property_group, "WarningLevel").text = "4" +ET.SubElement(property_group, "IsWebBootstrapper").text = "false" +ET.SubElement(property_group, "TargetFrameworkProfile") +ET.SubElement(property_group, "PublishUrl").text = "publish\\" +ET.SubElement(property_group, "Install").text = "true" +ET.SubElement(property_group, "InstallFrom").text = "Disk" +ET.SubElement(property_group, "UpdateEnabled").text = "false" +ET.SubElement(property_group, "UpdateMode").text = "Foreground" +ET.SubElement(property_group, "UpdateInterval").text = "7" +ET.SubElement(property_group, "UpdateIntervalUnits").text = "Days" +ET.SubElement(property_group, "UpdatePeriodically").text = "false" +ET.SubElement(property_group, "UpdateRequired").text = "false" +ET.SubElement(property_group, "MapFileExtensions").text = "true" +ET.SubElement(property_group, "ApplicationRevision").text = "0" +ET.SubElement(property_group, "ApplicationVersion").text = "1.0.0.%2a" +ET.SubElement(property_group, "UseApplicationTrust").text = "false" +ET.SubElement(property_group, "BootstrapperEnabled").text = "true" + +debug_property_group = ET.SubElement(root, "PropertyGroup", Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ") +ET.SubElement(debug_property_group, "PlatformTarget").text = "x64" +ET.SubElement(debug_property_group, "DebugSymbols").text = "true" +ET.SubElement(debug_property_group, "DebugType").text = "full" +ET.SubElement(debug_property_group, "Optimize").text = "false" +ET.SubElement(debug_property_group, "OutputPath").text = "bin\\Debug\\" +ET.SubElement(debug_property_group, "DefineConstants").text = "DEBUG;TRACE" +ET.SubElement(debug_property_group, "ErrorReport").text = "prompt" +ET.SubElement(debug_property_group, "WarningLevel").text = "4" +ET.SubElement(debug_property_group, "Prefer32Bit").text = "false" + +release_property_group = ET.SubElement(root, "PropertyGroup", Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ") +ET.SubElement(release_property_group, "PlatformTarget").text = "x64" +ET.SubElement(release_property_group, "DebugType").text = "pdbonly" +ET.SubElement(release_property_group, "Optimize").text = "true" +ET.SubElement(release_property_group, "OutputPath").text = "bin\\Release\\" +ET.SubElement(release_property_group, "DefineConstants").text = "TRACE" +ET.SubElement(release_property_group, "ErrorReport").text = "prompt" +ET.SubElement(release_property_group, "WarningLevel").text = "4" + +icon_property_group = ET.SubElement(root, "PropertyGroup") +ET.SubElement(icon_property_group, "ApplicationIcon").text = "SANS_Icon.ico" + +references = [ + ("Microsoft.Office.Interop.Excel, Version=15.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c, processorArchitecture=MSIL", "True"), + "ReachFramework", + "System", + "System.Data", + "System.Xml", + "Microsoft.CSharp", + "System.Core", + "System.Xml.Linq", + "System.Data.DataSetExtensions", + ("System.Xaml", "4.0"), + "WindowsBase", + "PresentationCore", + "PresentationFramework" +] + +item_group = ET.SubElement(root, "ItemGroup") +for ref in references: + if isinstance(ref, tuple): + reference = ET.SubElement(item_group, "Reference", Include=ref[0]) + ET.SubElement(reference, "EmbedInteropTypes").text = ref[1] + else: + ET.SubElement(item_group, "Reference", Include=ref) + +compile_files = [ + "Models\\DataCollection.py", + "Models\\InstrumentInfo.py", + "IO\\CsvExporter.py", + "Dialogs\\PreviewDialog.xaml.py", + "IO\\ExcelIO.py", + "Models\\A2Setting.py", + "Models\\Estimation.py", + "Models\\Geometry.py", + "Models\\RunOrder.py", + "EstimationPanel.xaml.py", + "Models\\Experiment.py", + "Models\\ExperimentSettings.py", + "Models\\WaitForUnits.py", + "Scripting\\GeniePythonScriptGenerator.py", + "Scripting\\IScriptGenerator.py", + "Scripting\\OpenGenieScriptGenerator.py", + "SettingsPanel.xaml.py", + "ViewModels\\DataGridVM.py", + "ViewModels\\EstimationPanelVM.py", + "ViewModels\\InstrumentVM.py", + "ViewModels\\SettingsPanelVM.py", + "App.xaml.py", + "MainWindow.xaml.py", + "Properties\\AssemblyInfo.py", + "Properties\\Resources.Designer.py", + "Properties\\Settings.Designer.py" +] + +for file in compile_files: + compile = ET.SubElement(item_group, "Compile", Include=file) + if file.endswith(".xaml.py"): + ET.SubElement(compile, "DependentUpon").text = file.replace(".py", "") + elif file.endswith(".Designer.py"): + ET.SubElement(compile, "AutoGen").text = "True" + ET.SubElement(compile, "DesignTime").text = "True" + ET.SubElement(compile, "DependentUpon").text = file.replace(".Designer.py", ".resx") + if "Settings" in file: + ET.SubElement(compile, "DesignTimeSharedInput").text = "True" + +pages = [ + "Dialogs\\PreviewDialog.xaml", + "EstimationPanel.xaml", + "MainWindow.xaml", + "SettingsPanel.xaml" +] + +for page in pages: + page_element = ET.SubElement(item_group, "Page", Include=page) + ET.SubElement(page_element, "SubType").text = "Designer" + ET.SubElement(page_element, "Generator").text = "MSBuild:Compile" + +embedded_resources = [ + "Properties\\Resources.resx" +] + +for resource in embedded_resources: + embedded_resource = ET.SubElement(item_group, "EmbeddedResource", Include=resource) + ET.SubElement(embedded_resource, "Generator").text = "ResXFileCodeGenerator" + ET.SubElement(embedded_resource, "LastGenOutput").text = resource.replace(".resx", ".Designer.py") + +none_files = [ + "Properties\\Settings.settings", + "App.config" +] + +for none_file in none_files: + none = ET.SubElement(item_group, "None", Include=none_file) + if none_file.endswith(".settings"): + ET.SubElement(none, "Generator").text = "SettingsSingleFileGenerator" + ET.SubElement(none, "LastGenOutput").text = none_file.replace(".settings", ".Designer.py") + +ET.SubElement(item_group, "AppDesigner", Include="Properties\\") + +bootstrapper_packages = [ + (".NETFramework,Version=v4.5", "Microsoft .NET Framework 4.5 %28x86 and x64%29", "true"), + ("Microsoft.Net.Framework.3.5.SP1", ".NET Framework 3.5 SP1", "false") +] + +for package in bootstrapper_packages: + bootstrapper_package = ET.SubElement(item_group, "BootstrapperPackage", Include=package[0]) + ET.SubElement(bootstrapper_package, "Visible").text = "False" + ET.SubElement(bootstrapper_package, "ProductName").text = package[1] + ET.SubElement(bootstrapper_package, "Install").text = package[2] + +ET.SubElement(item_group, "Resource", Include="SANS_Icon.ico") + +ET.SubElement(item_group, "WCFMetadata", Include="Connected Services\\") + +ET.SubElement(root, "Import", Project="$(MSBuildToolsPath)\\Microsoft.CSharp.targets") + +tree = ET.ElementTree(root) +tree.write("SANS_Script_GUI_v4.xml", encoding="utf-8", xml_declaration=True) \ No newline at end of file diff --git a/SANS_Script_GUI_v4.csproj.user.py b/SANS_Script_GUI_v4.csproj.user.py new file mode 100644 index 0000000..13474b5 --- /dev/null +++ b/SANS_Script_GUI_v4.csproj.user.py @@ -0,0 +1,16 @@ +import xml.etree.ElementTree as ET + +root = ET.Element("Project", ToolsVersion="14.0", xmlns="http://schemas.microsoft.com/developer/msbuild/2003") + +property_group = ET.SubElement(root, "PropertyGroup") +ET.SubElement(property_group, "PublishUrlHistory").text = "publish\\" +ET.SubElement(property_group, "InstallUrlHistory") +ET.SubElement(property_group, "SupportUrlHistory") +ET.SubElement(property_group, "UpdateUrlHistory") +ET.SubElement(property_group, "BootstrapperUrlHistory") +ET.SubElement(property_group, "ErrorReportUrlHistory") +ET.SubElement(property_group, "FallbackCulture").text = "en-US" +ET.SubElement(property_group, "VerifyUploadedFiles").text = "false" + +tree = ET.ElementTree(root) +tree.write("SANS_Script_GUI_v4.xml", encoding="utf-8", xml_declaration=True) \ No newline at end of file diff --git a/SANS_Script_GUI_v4.xml b/SANS_Script_GUI_v4.xml new file mode 100644 index 0000000..5e50bb7 --- /dev/null +++ b/SANS_Script_GUI_v4.xml @@ -0,0 +1,2 @@ + +DebugAnyCPU{5DBFF2C3-05A0-4B4B-9080-8224D86A7B40}WinExePropertiesLOQ_Script_GuiSANS_Script_Gui_v4v4.8512{60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}4falsepublish\trueDiskfalseForeground7Daysfalsefalsetrue01.0.0.%2afalsetruex64truefullfalsebin\Debug\DEBUG;TRACEprompt4falsex64pdbonlytruebin\Release\TRACEprompt4SANS_Icon.icoTrue4.0Dialogs\PreviewDialog.xamlEstimationPanel.xamlSettingsPanel.xamlApp.xamlMainWindow.xamlTrueTrueProperties\Resources.resxTrueTrueProperties\Settings.resxTrueDesignerMSBuild:CompileDesignerMSBuild:CompileDesignerMSBuild:CompileDesignerMSBuild:CompileResXFileCodeGeneratorProperties\Resources.Designer.csSettingsSingleFileGeneratorProperties\Settings.Designer.csFalseMicrosoft .NET Framework 4.5 %28x86 and x64%29trueFalse.NET Framework 3.5 SP1false \ No newline at end of file diff --git a/SANS_Script_Writer/SANS_Script_Writer.sln b/SANS_Script_Writer/SANS_Script_Writer.sln new file mode 100644 index 0000000..d5ff645 --- /dev/null +++ b/SANS_Script_Writer/SANS_Script_Writer.sln @@ -0,0 +1,20 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.12.35527.113 d17.12 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{888888A0-9F3D-457C-B088-3A5042F75D52}") = "SANS_Script_Writer", "SANS_Script_Writer\SANS_Script_Writer.pyproj", "{83C517F7-94B7-490E-9812-1A281BC3AABB}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {83C517F7-94B7-490E-9812-1A281BC3AABB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {83C517F7-94B7-490E-9812-1A281BC3AABB}.Release|Any CPU.ActiveCfg = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/SANS_Script_Writer/SANS_Script_Writer/SANS_Script_Writer.py b/SANS_Script_Writer/SANS_Script_Writer/SANS_Script_Writer.py new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/SANS_Script_Writer/SANS_Script_Writer/SANS_Script_Writer.py @@ -0,0 +1 @@ + diff --git a/SANS_Script_Writer/SANS_Script_Writer/SANS_Script_Writer.pyproj b/SANS_Script_Writer/SANS_Script_Writer/SANS_Script_Writer.pyproj new file mode 100644 index 0000000..b77f107 --- /dev/null +++ b/SANS_Script_Writer/SANS_Script_Writer/SANS_Script_Writer.pyproj @@ -0,0 +1,35 @@ + + + Debug + 2.0 + 83c517f7-94b7-490e-9812-1a281bc3aabb + . + SANS_Script_Writer.py + + + . + . + SANS_Script_Writer + SANS_Script_Writer + + + true + false + + + true + false + + + + + + + + + + + + \ No newline at end of file diff --git a/Scripting/GeniePythonScriptGenerator.cs b/Scripting/GeniePythonScriptGenerator.cs new file mode 100644 index 0000000..3db9a71 --- /dev/null +++ b/Scripting/GeniePythonScriptGenerator.cs @@ -0,0 +1,540 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Collections.ObjectModel; + +namespace LOQ_Script_Gui.Scripting +{ + class GeniePythonScriptGenerator : IScriptGenerator + { + string indent = " "; + + private bool isLarmor = false; + private bool isloq = false; + private bool issans2d = false; + private bool iszoom = false; + + public GeniePythonScriptGenerator(bool isLarmor, bool isloq, bool isZoom, bool isSans2d) + { + this.isLarmor = isLarmor; + this.isloq = isloq; + this.issans2d = isSans2d; + this.iszoom = isZoom; + } + + public string GenerateScript(ObservableCollection runs, ExperimentSettings settings, String scriptname) + { + string script = "# Script created by SANSScript at " + DateTime.Now + Environment.NewLine; + script += Environment.NewLine; + script += GenerateHeader(scriptname); + script += GenerateSetup(settings); + script += GenerateRuns(runs, settings); + script += Environment.NewLine; + + return script; + } + + private string GetIndentString(int n) + { + return new StringBuilder().Insert(0, indent, n).ToString(); + } + + private string GenerateHeader(String scriptname) + { + //string script = "import LSS.SANSroutines_v2 as lm" + Environment.NewLine ; + //string script = "from SansScripting import *" + Environment.NewLine; + //script += "@user_script" + Environment.NewLine + Environment.NewLine; + //script += Environment.NewLine; + string script = "import sys" + Environment.NewLine; + script += "from genie_python import genie as g, BLOCK_NAMES as b" + Environment.NewLine; + script += "sys.path.append(r\"c:\\instrument\\scripts\")" + Environment.NewLine; + + if (isLarmor) + { + script += "from instrument.larmor import * # pylint: disable=wildcard-import, unused-wildcard-import" + Environment.NewLine; + } + if (isloq) + { + script += "from instrument.loq import * # pylint: disable=wildcard-import, unused-wildcard-import" + Environment.NewLine; + } + if (iszoom) + { + script += "from instrument.zoom import * # pylint: disable=wildcard-import, unused-wildcard-import" + Environment.NewLine; + } + if (issans2d) + { + script += "from instrument.sans2d import * # pylint: disable=wildcard-import, unused-wildcard-import" + Environment.NewLine; + } + + script += Environment.NewLine; + script += "def "+scriptname+"():" + Environment.NewLine; + + return script; + } + + private string GenerateSetup(ExperimentSettings settings) + { + string script = ""; + + // TODO: Set number of available periods + + //script += GetIndentString(1) + "lm.setuplarmor_normal()" + Environment.NewLine; + script += GetIndentString(1) + string.Format("g.change_sample_par('Width', '{0}')", settings.SampleWidth) + Environment.NewLine; + script += GetIndentString(1) + string.Format("g.change_sample_par('height', '{0}')", settings.SampleHeight) + Environment.NewLine; + script += GetIndentString(1) + string.Format("g.change_sample_par('Geometry', '{0}')", settings.SampleGeometry) + Environment.NewLine + Environment.NewLine; + //script += GetIndentString(1) + string.Format("set_pv('IN:LARMOR:PARS:SAMPLE:WIDTH:SP', '{0}')", settings.SampleWidth) + Environment.NewLine; + //script += GetIndentString(1) + string.Format("set_pv('IN:LARMOR:PARS:SAMPLE:HEIGHT:SP', '{0}')", settings.SampleHeight) + Environment.NewLine; + //script += GetIndentString(1) + string.Format("set_pv('IN:LARMOR:PARS:SAMPLE:GEOMETRY:SP', '{0}')", settings.SampleGeometry) + Environment.NewLine + Environment.NewLine; + + return script; + } + + private string CheckPositions(ObservableCollection runs) + { + string script = ""; + + // Check that the positions are vailid + if (isLarmor) + { + string positionlist = "\""; + + foreach (Experiment exp in runs) + { + if (!string.IsNullOrWhiteSpace(exp.Position)) + { + positionlist += exp.Position + ","; + } + } + + if (positionlist.Length > 1) + { + if (positionlist.EndsWith(",")) + { + positionlist = positionlist.Substring(0, positionlist.Length - 1); + } + + positionlist += "\""; + + string tscript = string.Format("if check_move_pos([{0}]) != True", positionlist); + tscript = tscript.Replace(",", "\",\""); + script += GetIndentString(1) + tscript; + script += Environment.NewLine; + script += GetIndentString(2)+"print \"Invalid positions - script aborted\"" + Environment.NewLine; + script += GetIndentString(2)+"return" + Environment.NewLine + Environment.NewLine; + } + } + + //return script; + return ""; + } + private string GenerateRuns(ObservableCollection runs, ExperimentSettings settings) + { + string script = ""; + script = CheckPositions(runs); + + if (runs.Count == 0) + { + // Nothing to do + return script; + } + + if (settings.Order == RunOrder.AllTrans) + { + // All TRANS first + script += GetIndentString(1) + string.Format("for i in range({0}):", settings.NumTrans) + Environment.NewLine; + + foreach (Experiment exp in runs) + { + if (exp.Trans != 0) + { + int indent = 2; + script = DoTransRun(script, exp, indent, settings); + } + } + + // Then the SANS + script += GetIndentString(1) + string.Format("for i in range({0}):", settings.NumSans) + Environment.NewLine; + + foreach (Experiment exp in runs) + { + if (exp.Sans != 0) + { + int indent = 2; + script = DoSansRun(script, exp, indent, settings); + } + } + } + else if (settings.Order == RunOrder.AllSans) + { + // All SANS first + script += GetIndentString(1) + string.Format("for i in range({0}):", settings.NumSans) + Environment.NewLine; + + foreach (Experiment exp in runs) + { + if (exp.Sans != 0) + { + int indent = 2; + script = DoSansRun(script, exp, indent, settings); + } + } + + // Then the TRANS if we are running them + // check to see if there are actually any trans runs required by adding up all the trans count times + double sumTrans = 0.0; + foreach (Experiment exp in runs) + { + if (exp.Trans != 0) + { + sumTrans += exp.Trans; + } + } + + if (settings.NumTrans != 0 && sumTrans > 0.01) + { + script += GetIndentString(1) + string.Format("for i in range({0}):", settings.NumTrans) + Environment.NewLine; + } + foreach (Experiment exp in runs) + { + if (exp.Trans != 0) + { + int indent = 2; + script = DoTransRun(script, exp, indent, settings); + } + } + } + else if (settings.Order == RunOrder.TransFirst || settings.Order == RunOrder.SansFirst) + { + // TRANS - SANS - TRANS - SANS etc. or SANS - TRANS - SANS - TRANS etc + // Note: number of SANS and TRANS runs may be different + script += GetIndentString(1) + string.Format("num_sans = {0}", settings.NumSans) + Environment.NewLine; + script += GetIndentString(1) + string.Format("num_trans = {0}", settings.NumTrans) + Environment.NewLine; + script += GetIndentString(1) + "count = 0" + Environment.NewLine; + script += GetIndentString(1) + "while True:" + Environment.NewLine; + + int indent = 2; + + foreach (Experiment exp in runs) + { + if (settings.Order == RunOrder.TransFirst) + { + if (exp.Trans != 0) + { + script += GetIndentString(indent) + "if count < num_trans:" + Environment.NewLine; + script = DoTransRun(script, exp, indent + 1, settings); + } + if (exp.Sans != 0) + { + script += GetIndentString(indent) + "if count < num_sans:" + Environment.NewLine; + script = DoSansRun(script, exp, indent + 1, settings); + } + } + else + { + if (exp.Sans != 0) + { + script += GetIndentString(indent) + "if count < num_sans:" + Environment.NewLine; + script = DoSansRun(script, exp, indent + 1, settings); + } + if (exp.Trans != 0) + { + script += GetIndentString(indent) + "if count < num_trans:" + Environment.NewLine; + script = DoTransRun(script, exp, indent + 1, settings); + } + } + } + + script += GetIndentString(indent) + "count += 1" + Environment.NewLine; + script += GetIndentString(indent) + "if count >= num_trans and count >= num_sans: break" + Environment.NewLine; + } + + return script; + } + + private string DoTransRun(string script, Experiment exp, int indent, ExperimentSettings settings) + { + script += DoSampleEnvironment(exp, indent); + //script += SetAperture(settings.A2SettingTrans, indent); + + string waitfor = "uamps"; // Default to uamps + string rtype = "histogram"; // Default to event mode + string fulltitle = "{" + exp.Sample + "}"; + + if (settings.CollectionMode == DataCollection.Events) + { + rtype = "event"; + } + + if (exp.TransWait == WaitForUnits.Frames) + { + waitfor = "frames"; + } + else if (exp.TransWait == WaitForUnits.Seconds) + { + waitfor = "seconds"; + } + else if (exp.TransWait == WaitForUnits.Minutes) + { + waitfor = "minutes"; + } + + if (isLarmor == true || iszoom == true) + { + script += GetIndentString(indent) + string.Format("do_trans(title='{1}',pos='{0}', {2}={3}, thickness={4})", + exp.Position, fulltitle, waitfor, exp.Trans, exp.Thickness) + Environment.NewLine; + } + else if(isloq == true) + { + script += GetIndentString(indent) + string.Format("do_trans(title='{1}',pos='{0}', {2}={3}, thickness={4}, aperture='small')", + exp.Position, fulltitle, waitfor, exp.Trans, exp.Thickness) + Environment.NewLine; + } + else + { + script += GetIndentString(indent) + string.Format("do_trans(title='{1}',pos='{0}', {2}={3}, thickness={4}, dae='transmission', aperture='{5}')", + exp.Position, fulltitle, waitfor, exp.Trans, exp.Thickness,ApertureString(settings.A2SettingTrans)) + Environment.NewLine; + } + + script += GetIndentString(indent) + exp.PostCommand + Environment.NewLine; + return script; + } + + private string DoSansRun(string script, Experiment exp, int indent, ExperimentSettings settings) + { + script += DoSampleEnvironment(exp, indent); + //script += SetAperture(settings.A2SettingSans, indent); + + string waitfor = "uamps"; // Default to uamps + string rtype = "histogram"; // Default to histogram mode + string fulltitle = "{" + exp.Sample + "}"; + + if (!string.IsNullOrWhiteSpace(exp.Background)) + { + fulltitle += "_{" + exp.Background + "}"; + } + + if (settings.CollectionMode == DataCollection.Events || settings.CollectionMode == DataCollection.TRANSSANS_Events) + { + rtype = "event"; + } + else if (settings.CollectionMode == DataCollection.SESANS) + { + rtype = "sesans"; + } + if (settings.CollectionMode == DataCollection.TRANSSANS_Events || settings.CollectionMode == DataCollection.TRANSSANS_Histogram) + { + // add TRANS to the title so that when SANS is added by do_sans we get _TRANS_SANS + fulltitle += "_TRANS"; + } + + if (exp.SansWait == WaitForUnits.Frames) + { + waitfor = "frames"; + } + else if (exp.SansWait == WaitForUnits.Seconds) + { + waitfor = "seconds"; + } + else if (exp.SansWait == WaitForUnits.Minutes) + { + waitfor = "minutes"; + } + + if (isLarmor == true || iszoom == true) + { + script += GetIndentString(indent) + string.Format("do_sans(title='{1}',pos='{0}', {2}={3}, thickness={4}, dae='{5}')", + exp.Position, fulltitle, waitfor, exp.Sans, exp.Thickness, rtype) + Environment.NewLine; + } + else + { + script += GetIndentString(indent) + string.Format("do_sans(title='{1}',pos='{0}', {2}={3}, thickness={4}, dae='{5}',aperture='{6}')", + exp.Position, fulltitle, waitfor, exp.Sans, exp.Thickness, rtype,ApertureString(settings.A2SettingSans)) + Environment.NewLine; + } + + script += GetIndentString(indent) + exp.PostCommand + Environment.NewLine; + return script; + } + + private string SetAperture(string size, int indent) + { + string script = ""; + if (size == A2Setting.Large || size == A2Setting.Larmor) + { + script += GetIndentString(indent) + "set_aperture('large')" + Environment.NewLine; + } + else if (size == A2Setting.Medium || size == A2Setting.Larmor) + { + script += GetIndentString(indent) + "set_aperture('medium')" + Environment.NewLine; + } + else if (size == A2Setting.Small || size == A2Setting.Larmor) + { + script += GetIndentString(indent) + "set_aperture('small')" + Environment.NewLine; + } + else if (size == A2Setting.XLarge || size == A2Setting.Larmor) + { + script += GetIndentString(indent) + "set_aperture('xlarge')" + Environment.NewLine; + } + return script; + } + + private string ApertureString(string size) + { + string script = ""; + if (size == A2Setting.Large || size == A2Setting.Larmor) + { + script = "large"; + } + else if (size == A2Setting.Medium || size == A2Setting.Larmor) + { + script = "medium"; + } + else if (size == A2Setting.Small || size == A2Setting.Larmor) + { + script = "small"; + } + else if (size == A2Setting.XLarge || size == A2Setting.Larmor) + { + script = "xlarge"; + } + return script; + } + + private string DoSampleEnvironment(Experiment exp, int indent) + { + string script = ""; + + // Set Temps first + if (!string.IsNullOrWhiteSpace(exp.Temperature1)) + { + double temp1 = Convert.ToDouble(exp.Temperature1); + double temp1low = temp1-1.0; + double temp1high = temp1 + 1.0; + if (isloq == true) + { + script += GetIndentString(indent) + string.Format("g.cset(internal_setpoint_1={0})", exp.Temperature1) + Environment.NewLine; + script += GetIndentString(indent) + string.Format("g.cset(\"Julabo_1_ext\",lowlimit={0},highlimit={1},runcontrol=True)", Convert.ToString(temp1low), Convert.ToString(temp1high)) + Environment.NewLine; + }else + { + script += GetIndentString(indent) + string.Format("g.cset(JULABO1_SP={0})", exp.Temperature1) + Environment.NewLine; + script += GetIndentString(indent) + string.Format("g.cset(\"JULABO1_TEMP\",lowlimit={0},highlimit={1},runcontrol=True)", Convert.ToString(temp1low), Convert.ToString(temp1high)) + Environment.NewLine; + } + } + + if (!string.IsNullOrWhiteSpace(exp.Temperature2)) + { + double temp2 = Convert.ToDouble(exp.Temperature2); + double temp2low = temp2 - 1.0; + double temp2high = temp2 + 1.0; + if (isloq == true) + { + script += GetIndentString(indent) + string.Format("g.cset(internal_setpoint_2={0})", exp.Temperature2) + Environment.NewLine; + script += GetIndentString(indent) + string.Format("g.cset(\"Julabo_2_ext\",lowlimit={0},highlimit={1},runcontrol=True)", Convert.ToString(temp2low), Convert.ToString(temp2high)) + Environment.NewLine; + } + else + { + script += GetIndentString(indent) + string.Format("g.cset(JULABO2_SP={0})", exp.Temperature2) + Environment.NewLine; + script += GetIndentString(indent) + string.Format("g.cset(\"JULABO2_TEMP\",lowlimit={0},highlimit={1},runcontrol=True)", Convert.ToString(temp2low), Convert.ToString(temp2high)) + Environment.NewLine; + } + } + + // Check for pre-command - always run immediately after the temp is set + if (!string.IsNullOrWhiteSpace(exp.PreCommand)) + { + script += GetIndentString(indent) + exp.PreCommand + Environment.NewLine; + } + + if (!string.IsNullOrWhiteSpace(exp.Field)) + { + script += GetIndentString(indent) + string.Format("g.cset(FIELD={0})", exp.Field) + Environment.NewLine; + } + + if (!string.IsNullOrWhiteSpace(exp.ShearRate1)) + { + script += GetIndentString(indent) + string.Format("g.cset(SHEAR_RATE_1={0})", exp.ShearRate1) + Environment.NewLine; + } + + if (!string.IsNullOrWhiteSpace(exp.ShearRate2)) + { + script += GetIndentString(indent) + string.Format("g.cset(SHEAR_RATE_2={0})", exp.ShearRate2) + Environment.NewLine; + } + + if (!string.IsNullOrWhiteSpace(exp.ShearAngle1)) + { + script += GetIndentString(indent) + string.Format("g.cset(SHEAR_ANGLE_1={0})", exp.ShearAngle1) + Environment.NewLine; + } + + if (!string.IsNullOrWhiteSpace(exp.ShearAngle2)) + { + script += GetIndentString(indent) + string.Format("g.cset(SHEAR_ANGLE_2={0})", exp.ShearAngle1) + Environment.NewLine; + } + + return script; + } + + public string EstimateRunTime(ObservableCollection runs, ExperimentSettings settings, Estimation estimation) + { + double minutes = 0; + + if (settings.NumSans > 0) + { + minutes += CalculateTime(runs, true, estimation.CountRate, estimation.MoveTime) * settings.NumSans; + } + + if (settings.NumTrans > 0) + { + minutes += CalculateTime(runs, false, estimation.CountRate, estimation.MoveTime) * settings.NumTrans; + } + + DateTime time = new DateTime(1, 1, 1, 0, 0, 0); + + time = time.AddMinutes(minutes); + + return time.ToLongTimeString(); + } + + private static double CalculateTime(ObservableCollection runs, bool isSans, double countRate, double secondsBetween) + { + double minutes = 0; + + foreach (Experiment exp in runs) + { + if (!string.IsNullOrWhiteSpace(exp.Position)) + { + if (isSans) + { + minutes += GetMinutes(exp.SansWait, exp.Sans, countRate); + } + else + { + minutes += GetMinutes(exp.TransWait, exp.Trans, countRate); + } + + minutes += secondsBetween / 60; + } + } + + return minutes; + } + + private static double GetMinutes(string wait, double length, double countRate) + { + if (wait == WaitForUnits.Minutes) + { + return length; + } + else if (wait == WaitForUnits.Seconds) + { + return length / 60.0; + } + else if (wait == WaitForUnits.Frames) + { + return length / (25.0 * 60.0); + } + else + { + // If in doubt use micro-amps + return length / (countRate / 60.0); + } + } + } +} diff --git a/Scripting/GeniePythonScriptGenerator.py b/Scripting/GeniePythonScriptGenerator.py new file mode 100644 index 0000000..51ca3fe --- /dev/null +++ b/Scripting/GeniePythonScriptGenerator.py @@ -0,0 +1,256 @@ +from datetime import datetime, timedelta +from Models.Estimation import Estimation +from Models.Experiment import Experiment +from Models.ExperimentSettings import ExperimentSettings + +class GeniePythonScriptGenerator: + def __init__(self, is_larmor, is_loq, is_zoom, is_sans2d): + self.indent = " " + self.is_larmor = is_larmor + self.is_loq = is_loq + self.is_zoom = is_zoom + self.is_sans2d = is_sans2d + + def generate_script(self, runs, settings, scriptname): + script = f"# Script created by SANSScript at {datetime.now()}\n\n" + script += self.generate_header(scriptname) + script += self.generate_setup(settings) + script += self.generate_runs(runs, settings) + script += "\n" + return script + + def get_indent_string(self, n): + return self.indent * n + + def generate_header(self, scriptname): + script = "import sys\n" + script += "from genie_python import genie as g, BLOCK_NAMES as b\n" + script += "sys.path.append(r\"c:\\instrument\\scripts\")\n" + + if self.is_larmor: + script += "from instrument.larmor import * # pylint: disable=wildcard-import, unused-wildcard-import\n" + if self.is_loq: + script += "from instrument.loq import * # pylint: disable=wildcard-import, unused-wildcard-import\n" + if self.is_zoom: + script += "from instrument.zoom import * # pylint: disable=wildcard-import, unused-wildcard-import\n" + if self.is_sans2d: + script += "from instrument.sans2d import * # pylint: disable=wildcard-import, unused-wildcard-import\n" + + script += f"\ndef {scriptname}():\n" + return script + + def generate_setup(self, settings): + script = "" + script += self.get_indent_string(1) + f"g.change_sample_par('Width', '{settings.sample_width}')\n" + script += self.get_indent_string(1) + f"g.change_sample_par('height', '{settings.sample_height}')\n" + script += self.get_indent_string(1) + f"g.change_sample_par('Geometry', '{settings.sample_geometry}')\n\n" + return script + + def check_positions(self, runs): + script = "" + if self.is_larmor: + position_list = ",".join([exp.position for exp in runs if exp.position]) + if position_list: + script += self.get_indent_string(1) + f"if check_move_pos(['{position_list}']) != True:\n" + script += self.get_indent_string(2) + "print('Invalid positions - script aborted')\n" + script += self.get_indent_string(2) + "return\n\n" + return script + + def generate_runs(self, runs, settings): + script = self.check_positions(runs) + if not runs: + return script + + if settings.order == "AllTrans": + script += self.get_indent_string(1) + f"for i in range({settings.num_trans}):\n" + for exp in runs: + if exp.trans: + script = self.do_trans_run(script, exp, 2, settings) + script += self.get_indent_string(1) + f"for i in range({settings.num_sans}):\n" + for exp in runs: + if exp.sans: + script = self.do_sans_run(script, exp, 2, settings) + elif settings.order == "AllSans": + script += self.get_indent_string(1) + f"for i in range({settings.num_sans}):\n" + for exp in runs: + if exp.sans: + script = self.do_sans_run(script, exp, 2, settings) + if settings.num_trans and any(exp.trans for exp in runs): + script += self.get_indent_string(1) + f"for i in range({settings.num_trans}):\n" + for exp in runs: + if exp.trans: + script = self.do_trans_run(script, exp, 2, settings) + else: + script += self.get_indent_string(1) + f"num_sans = {settings.num_sans}\n" + script += self.get_indent_string(1) + f"num_trans = {settings.num_trans}\n" + script += self.get_indent_string(1) + "count = 0\n" + script += self.get_indent_string(1) + "while True:\n" + indent = 2 + for exp in runs: + if settings.order == "TransFirst": + if exp.trans: + script += self.get_indent_string(indent) + "if count < num_trans:\n" + script = self.do_trans_run(script, exp, indent + 1, settings) + if exp.sans: + script += self.get_indent_string(indent) + "if count < num_sans:\n" + script = self.do_sans_run(script, exp, indent + 1, settings) + else: + if exp.sans: + script += self.get_indent_string(indent) + "if count < num_sans:\n" + script = self.do_sans_run(script, exp, indent + 1, settings) + if exp.trans: + script += self.get_indent_string(indent) + "if count < num_trans:\n" + script = self.do_trans_run(script, exp, indent + 1, settings) + script += self.get_indent_string(indent) + "count += 1\n" + script += self.get_indent_string(indent) + "if count >= num_trans and count >= num_sans: break\n" + return script + + def do_trans_run(self, script, exp, indent, settings): + script += self.do_sample_environment(exp, indent) + waitfor = "uamps" + rtype = "histogram" + fulltitle = f"{{{exp.sample}}}" + if settings.collection_mode == "Events": + rtype = "event" + if exp.trans_wait == "Frames": + waitfor = "frames" + elif exp.trans_wait == "Seconds": + waitfor = "seconds" + elif exp.trans_wait == "Minutes": + waitfor = "minutes" + if self.is_larmor or self.is_zoom: + script += self.get_indent_string(indent) + f"do_trans(title='{fulltitle}', pos='{exp.position}', {waitfor}={exp.trans}, thickness={exp.thickness})\n" + elif self.is_loq: + script += self.get_indent_string(indent) + f"do_trans(title='{fulltitle}', pos='{exp.position}', {waitfor}={exp.trans}, thickness={exp.thickness}, aperture='small')\n" + else: + script += self.get_indent_string(indent) + f"do_trans(title='{fulltitle}', pos='{exp.position}', {waitfor}={exp.trans}, thickness={exp.thickness}, dae='transmission', aperture='{self.aperture_string(settings.a2_setting_trans)}')\n" + script += self.get_indent_string(indent) + exp.post_command + "\n" + return script + + def do_sans_run(self, script, exp, indent, settings): + script += self.do_sample_environment(exp, indent) + waitfor = "uamps" + rtype = "histogram" + fulltitle = f"{{{exp.sample}}}" + if exp.background: + fulltitle += f"_{{{exp.background}}}" + if settings.collection_mode in ["Events", "TRANSSANS_Events"]: + rtype = "event" + elif settings.collection_mode == "SESANS": + rtype = "sesans" + if settings.collection_mode in ["TRANSSANS_Events", "TRANSSANS_Histogram"]: + fulltitle += "_TRANS" + if exp.sans_wait == "Frames": + waitfor = "frames" + elif exp.sans_wait == "Seconds": + waitfor = "seconds" + elif exp.sans_wait == "Minutes": + waitfor = "minutes" + if self.is_larmor or self.is_zoom: + script += self.get_indent_string(indent) + f"do_sans(title='{fulltitle}', pos='{exp.position}', {waitfor}={exp.sans}, thickness={exp.thickness}, dae='{rtype}')\n" + else: + script += self.get_indent_string(indent) + f"do_sans(title='{fulltitle}', pos='{exp.position}', {waitfor}={exp.sans}, thickness={exp.thickness}, dae='{rtype}', aperture='{self.aperture_string(settings.a2_setting_sans)}')\n" + script += self.get_indent_string(indent) + exp.post_command + "\n" + return script + + def aperture_string(self, size): + if size in [A2Setting.Large, A2Setting.Larmor]: + return "large" + elif size in [A2Setting.Medium, A2Setting.Larmor]: + return "medium" + elif size in [A2Setting.Small, A2Setting.Larmor]: + return "small" + elif size in [A2Setting.XLarge, A2Setting.Larmor]: + return "xlarge" + return "" + + def do_sample_environment(self, exp, indent): + script = "" + if exp.temperature1: + temp1 = float(exp.temperature1) + temp1low = temp1 - 1.0 + temp1high = temp1 + 1.0 + if self.is_loq: + script += self.get_indent_string(indent) + f"g.cset(internal_setpoint_1={exp.temperature1})\n" + script += self.get_indent_string(indent) + f"g.cset(\"Julabo_1_ext\", lowlimit={temp1low}, highlimit={temp1high}, runcontrol=True)\n" + else: + script += self.get_indent_string(indent) + f"g.cset(JULABO1_SP={exp.temperature1})\n" + script += self.get_indent_string(indent) + f"g.cset(\"JULABO1_TEMP\", lowlimit={temp1low}, highlimit={temp1high}, runcontrol=True)\n" + if exp.temperature2: + temp2 = float(exp.temperature2) + temp2low = temp2 - 1.0 + temp2high = temp2 + 1.0 + if self.is_loq: + script += self.get_indent_string(indent) + f"g.cset(internal_setpoint_2={exp.temperature2})\n" + script += self.get_indent_string(indent) + f"g.cset(\"Julabo_2_ext\", lowlimit={temp2low}, highlimit={temp2high}, runcontrol=True)\n" + else: + script += self.get_indent_string(indent) + f"g.cset(JULABO2_SP={exp.temperature2})\n" + script += self.get_indent_string(indent) + f"g.cset(\"JULABO2_TEMP\", lowlimit={temp2low}, highlimit={temp2high}, runcontrol=True)\n" + if exp.pre_command: + script += self.get_indent_string(indent) + exp.pre_command + "\n" + if exp.field: + script += self.get_indent_string(indent) + f"g.cset(FIELD={exp.field})\n" + if exp.shear_rate1: + script += self.get_indent_string(indent) + f"g.cset(SHEAR_RATE_1={exp.shear_rate1})\n" + if exp.shear_rate2: + script += self.get_indent_string(indent) + f"g.cset(SHEAR_RATE_2={exp.shear_rate2})\n" + if exp.shear_angle1: + script += self.get_indent_string(indent) + f"g.cset(SHEAR_ANGLE_1={exp.shear_angle1})\n" + if exp.shear_angle2: + script += self.get_indent_string(indent) + f"g.cset(SHEAR_ANGLE_2={exp.shear_angle2})\n" + return script + + def estimate_run_time(self, runs, settings, estimation): + minutes = 0 + if settings.num_sans > 0: + minutes += self.calculate_time(runs, True, estimation.count_rate, estimation.move_time) * settings.num_sans + if settings.num_trans > 0: + minutes += self.calculate_time(runs, False, estimation.count_rate, estimation.move_time) * settings.num_trans + time = datetime(1, 1, 1, 0, 0, 0) + timedelta(minutes=minutes) + return time.strftime("%H:%M:%S") + + @staticmethod + def calculate_time(runs, is_sans, count_rate, seconds_between): + minutes = 0 + for exp in runs: + if exp.position: + if is_sans: + minutes += GeniePythonScriptGenerator.get_minutes(exp.sans_wait, exp.sans, count_rate) + else: + minutes += GeniePythonScriptGenerator.get_minutes(exp.trans_wait, exp.trans, count_rate) + minutes += seconds_between / 60 + return minutes + + @staticmethod + def get_minutes(wait, length, count_rate): + if wait == "Minutes": + return length + elif wait == "Seconds": + return length / 60.0 + elif wait == "Frames": + return length / (25.0 * 60.0) + else: + return length / (count_rate / 60.0) + +class A2Setting: + Large = "Large" + Medium = "Medium" + Small = "Small" + XLarge = "XLarge" + Larmor = "Larmor" + + + + + + + +if __name__ == "__main__": + runs = [Experiment(position="pos1", trans=10, sans=20, trans_wait="Minutes", sans_wait="Minutes", sample="Sample1", thickness=1.0)] + settings = ExperimentSettings(sample_width="10", sample_height="10", sample_geometry="Flat", order="AllTrans", num_trans=1, num_sans=1, collection_mode="Events", a2_setting_trans="Large", a2_setting_sans="Large") + estimation = Estimation(count_rate=100, move_time=10) + generator = GeniePythonScriptGenerator(is_larmor=True, is_loq=False, is_zoom=False, is_sans2d=False) + script = generator.generate_script(runs, settings, "my_script") + print(script) + estimated_time = generator.estimate_run_time(runs, settings, estimation) + print(f"Estimated run time: {estimated_time}") \ No newline at end of file diff --git a/Scripting/IScriptGenerator.cs b/Scripting/IScriptGenerator.cs new file mode 100644 index 0000000..fdddd91 --- /dev/null +++ b/Scripting/IScriptGenerator.cs @@ -0,0 +1,11 @@ +using System; +using System.Collections.ObjectModel; + +namespace LOQ_Script_Gui.Scripting +{ + interface IScriptGenerator + { + string GenerateScript(ObservableCollection runs, ExperimentSettings settings, String scriptname); + string EstimateRunTime(ObservableCollection runs, ExperimentSettings settings, Estimation estimation); + } +} diff --git a/Scripting/IScriptGenerator.py b/Scripting/IScriptGenerator.py new file mode 100644 index 0000000..4b8aca1 --- /dev/null +++ b/Scripting/IScriptGenerator.py @@ -0,0 +1,10 @@ +from abc import ABC, abstractmethod + +class IScriptGenerator(ABC): + @abstractmethod + def generate_script(self, runs, settings, scriptname): + pass + + @abstractmethod + def estimate_run_time(self, runs, settings, estimation): + pass diff --git a/Scripting/OpenGenieScriptGenerator.cs b/Scripting/OpenGenieScriptGenerator.cs new file mode 100644 index 0000000..9c9f5b7 --- /dev/null +++ b/Scripting/OpenGenieScriptGenerator.cs @@ -0,0 +1,627 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Collections.ObjectModel; + +namespace LOQ_Script_Gui.Scripting +{ + class OpenGenieScriptGenerator : IScriptGenerator + { + private bool isSans2d = false; + + public OpenGenieScriptGenerator(bool isSans2d) + { + this.isSans2d = isSans2d; + } + + public string GenerateScript(ObservableCollection runs, ExperimentSettings settings, String scriptname) + { + string script = "# Script created by LOQ Script at " + DateTime.Now + Environment.NewLine; + script += "# Script MUST be loaded in to Open GENIE using the loadscript command." + Environment.NewLine + Environment.NewLine; + + if (isSans2d) + { + script += "QUALIFIERS /test" + Environment.NewLine; + } + + script += string.Format("LOCAL transCount = 0 sansCount = 0 numTRANS={0} numSANS={1}", settings.NumTrans, settings.NumSans); + script += Environment.NewLine + Environment.NewLine; + + script += "SETSCRIPTNAME THIS_PROCEDURE()" + Environment.NewLine + Environment.NewLine; + + script += string.Format("Sample_par width={0} height={1} geometry=\"{2}\"", settings.SampleWidth, settings.SampleHeight, settings.SampleGeometry); + script += Environment.NewLine + Environment.NewLine; + + if (isSans2d) + { + string positionlist = "\""; + + foreach (Experiment exp in runs) + { + if (!string.IsNullOrWhiteSpace(exp.Position)) + { + positionlist += exp.Position + ","; + } + } + + if (positionlist.Length > 1) + { + if (positionlist.EndsWith(",")) + { + positionlist = positionlist.Substring(0, positionlist.Length - 1); + } + + positionlist += "\""; + + script += string.Format("IF NOT CHECK_MOVE_POS({0})", positionlist); + script += Environment.NewLine; + script += " PRINTEN \"Invalid positions - script aborted\"" + Environment.NewLine; + script += " RETURN" + Environment.NewLine; + script += "ENDIF" + Environment.NewLine + Environment.NewLine; + + } + + script += "IF test" + Environment.NewLine; + script += " RETURN" + Environment.NewLine; + script += "ENDIF" + Environment.NewLine + Environment.NewLine; + } + + if (settings.Order == RunOrder.AllTrans) + { + // Do all trans first + if (settings.LoopPerRun) + { + script += DoAllTransLoopEach(runs, settings, true, true); + script += DoAllSansLoopEach(runs, settings, true, true); + } + else + { + script += "LOOP" + Environment.NewLine + Environment.NewLine; + script += DoAllTrans(runs, settings, true, true); + script += DoAllSans(runs, settings, true, true); + script += "EXITIF ((transCount >= numTRANS) AND (sansCount >= numSANS))" + Environment.NewLine + Environment.NewLine; + script += "ENDLOOP" + Environment.NewLine + Environment.NewLine; + } + } + else if (settings.Order == RunOrder.TransFirst) + { + // Do Trans-Sans-Trans-Sans... + if (settings.LoopPerRun) + { + foreach (Experiment exp in runs) + { + script += DoSingleTransLoopEach(exp, settings, true, false); + script += DoSingleSansLoopEach(exp, settings, false, true); + } + } + else + { + script += "LOOP" + Environment.NewLine + Environment.NewLine; + + foreach (Experiment exp in runs) + { + script += DoSingleTrans(exp, settings, true, false); + script += DoSingleSans(exp, settings, false, true); + } + + script += "transCount = transCount + 1" + Environment.NewLine; + script += "sansCount = sansCount + 1" + Environment.NewLine + Environment.NewLine; + script += "EXITIF ((transCount >= numTRANS) AND (sansCount >= numSANS))" + Environment.NewLine + Environment.NewLine; + script += "ENDLOOP" + Environment.NewLine + Environment.NewLine; + } + } + else if (settings.Order == RunOrder.AllSans) + { + // Do All Sans first + if (settings.LoopPerRun) + { + script += DoAllSansLoopEach(runs, settings, true, true); + script += DoAllTransLoopEach(runs, settings, true, true); + } + else + { + script += "LOOP" + Environment.NewLine + Environment.NewLine; + script += DoAllSans(runs, settings, true, true); + script += DoAllTrans(runs, settings, true, true); + script += "EXITIF ((transCount >= numTRANS) AND (sansCount >= numSANS))" + Environment.NewLine + Environment.NewLine; + script += "ENDLOOP" + Environment.NewLine + Environment.NewLine; + } + } + else if (settings.Order == RunOrder.SansFirst) + { + // Do Sans-Trans-Sans-Trans... + if (settings.LoopPerRun) + { + foreach (Experiment exp in runs) + { + script += DoSingleSansLoopEach(exp, settings, true, false); + script += DoSingleTransLoopEach(exp, settings, false, true); + } + } + else + { + script += "LOOP" + Environment.NewLine + Environment.NewLine; + + foreach (Experiment exp in runs) + { + script += DoSingleSans(exp, settings, true, false); + script += DoSingleTrans(exp, settings, false, true); + } + + script += "transCount = transCount + 1" + Environment.NewLine; + script += "sansCount = sansCount + 1" + Environment.NewLine + Environment.NewLine; + script += "EXITIF ((transCount >= numTRANS) AND (sansCount >= numSANS))" + Environment.NewLine + Environment.NewLine; + script += "ENDLOOP" + Environment.NewLine + Environment.NewLine; + } + } + + //script += "ENDPROCEDURE" + Environment.NewLine; + script += "PRINTN \"SCRIPT COMPLETED \"" + Environment.NewLine + Environment.NewLine; + + return script; + } + + private static string DoSingleSans(Experiment exp, ExperimentSettings settings, bool doPreCom, bool doPostCom) + { + string script = ""; + + if (settings.NumSans > 0) + { + if (exp.Sans > 0) + { + script += "IF (sansCount < numSANS)" + Environment.NewLine; + script += setSansAperture(settings.A2SettingSans); + script += DoSingleRun(false, exp, false, doPreCom, doPostCom, settings); + script += Environment.NewLine + "ENDIF" + Environment.NewLine + Environment.NewLine; + } + } + return script; + } + + private static string DoSingleTrans(Experiment exp, ExperimentSettings settings, bool doPreCom, bool doPostCom) + { + string script = ""; + + if (settings.NumTrans > 0) + { + if (exp.Trans > 0) + { + script += "IF (transCount < numTRANS)" + Environment.NewLine; + script += setTransAperture(settings.A2SettingTrans); + script += DoSingleRun(true, exp, false, doPreCom, doPostCom, settings); + script += Environment.NewLine + "ENDIF" + Environment.NewLine + Environment.NewLine; + } + } + return script; + } + + private static string DoSingleSansLoopEach(Experiment exp, ExperimentSettings settings, bool doPreCom, bool doPostCom) + { + string script = ""; + + if (settings.NumSans > 0) + { + if (exp.Sans > 0) + { + script += setSansAperture(settings.A2SettingSans); + script += DoSingleRun(false, exp, true, doPreCom, doPostCom, settings) + Environment.NewLine; + } + } + return script; + } + + private static string DoSingleTransLoopEach(Experiment exp, ExperimentSettings settings, bool doPreCom, bool doPostCom) + { + string script = ""; + + if (settings.NumTrans > 0) + { + if (exp.Trans > 0) + { + script += setTransAperture(settings.A2SettingTrans); + script += DoSingleRun(true, exp, true, doPreCom, doPostCom,settings) + Environment.NewLine; + } + } + return script; + } + + private static string DoAllSans(ObservableCollection runs, ExperimentSettings settings, bool doPreCom, bool doPostCom) + { + string script = ""; + + if (settings.NumSans > 0) + { + script += setSansAperture(settings.A2SettingSans); + + script += "IF sansCount < numSANS" + Environment.NewLine + Environment.NewLine; + + // Loop through each experiment + foreach (Experiment exp in runs) + { + script += DoSingleRun(false, exp, false, doPreCom, doPostCom, settings) + Environment.NewLine; + } + + script += "sansCount = sansCount + 1" + Environment.NewLine + Environment.NewLine; + script += "ENDIF" + Environment.NewLine + Environment.NewLine; + } + + return script; + } + + private static string DoAllTrans(ObservableCollection runs, ExperimentSettings settings, bool doPreCom, bool doPostCom) + { + string script = ""; + + if (settings.NumTrans > 0) + { + script += setTransAperture(settings.A2SettingTrans); + + script += "IF transCount < numTRANS" + Environment.NewLine + Environment.NewLine; + + // Loop through each experiment + foreach (Experiment exp in runs) + { + script += DoSingleRun(true, exp, false, doPreCom, doPostCom, settings) + Environment.NewLine; + } + + script += "transCount = transCount + 1" + Environment.NewLine + Environment.NewLine; + script += "ENDIF" + Environment.NewLine + Environment.NewLine; + } + + return script; + } + + private static string DoAllSansLoopEach(ObservableCollection runs, ExperimentSettings settings, bool doPreCom, bool doPostCom) + { + string script = ""; + + if (settings.NumSans > 0) + { + script += setSansAperture(settings.A2SettingSans); + + // Loop through each experiment + foreach (Experiment exp in runs) + { + script += DoSingleRun(false, exp, true, doPreCom, doPostCom, settings) + Environment.NewLine; + } + } + + return script; + } + + private static string DoAllTransLoopEach(ObservableCollection runs, ExperimentSettings settings, bool doPreCom, bool doPostCom) + { + string script = ""; + + if (settings.NumTrans > 0) + { + script += setTransAperture(settings.A2SettingTrans); + + // Loop through each experiment + foreach (Experiment exp in runs) + { + script += DoSingleRun(true, exp, true, doPreCom, doPostCom, settings) + Environment.NewLine; + } + } + + return script; + } + + private static string setSansAperture(string A2SizeSans) + { + string script = ""; + + if (A2SizeSans == A2Setting.Large) + { + script += "DO_SANS /LARGE" + Environment.NewLine + Environment.NewLine; + } + else if (A2SizeSans == A2Setting.Medium) + { + script += "DO_SANS /MEDIUM" + Environment.NewLine + Environment.NewLine; + } + else if (A2SizeSans == A2Setting.Small) + { + script += "DO_SANS /SMALL" + Environment.NewLine + Environment.NewLine; + } + else if (A2SizeSans == A2Setting.Trans) + { + script += "DO_SANS /TRANS" + Environment.NewLine + Environment.NewLine; + } + + return script; + } + + private static string setTransAperture(string A2SizeTrans) + { + string script = ""; + + if (string.IsNullOrWhiteSpace(A2SizeTrans)) + { + script += "DO_TRANS" + Environment.NewLine + Environment.NewLine; + } + else + { + if (A2SizeTrans == A2Setting.Large) + { + script += "DO_TRANS /LARGE" + Environment.NewLine + Environment.NewLine; + } + else if (A2SizeTrans == A2Setting.Medium) + { + script += "DO_TRANS /MEDIUM" + Environment.NewLine + Environment.NewLine; + } + else if (A2SizeTrans == A2Setting.Small) + { + script += "DO_TRANS /SMALL" + Environment.NewLine + Environment.NewLine; + } + else if (A2SizeTrans == A2Setting.Trans) + { + script += "DO_TRANS /TRANS" + Environment.NewLine + Environment.NewLine; + } + } + + return script; + } + + private static string DoSingleRun(bool isTrans, Experiment exp, bool isLoop, bool doPreCom, bool doPostCom, ExperimentSettings settings) + { + string script = ""; + + if (exp.Trans > 0 || exp.Sans > 0) + { + if (!string.IsNullOrWhiteSpace(exp.RbNumber)) + { + script += "change rbno=" + exp.RbNumber + Environment.NewLine; + } + + if (!string.IsNullOrWhiteSpace(exp.Position)) + { + if (doPreCom) + { + script += DoSampleEnvironment(exp); + } + + if (isLoop) + { + if (isTrans) + { + script += "LOOP transCount FROM 1 TO numTRANS" + Environment.NewLine; + } + else + { + script += "LOOP sansCount FROM 1 TO numSANS" + Environment.NewLine; + } + + script += CreateMoveCommand(isTrans, exp, settings); + script += "ENDLOOP" + Environment.NewLine; + } + else + { + script += CreateMoveCommand(isTrans, exp, settings); + } + + // Check for post-command - always run last + if (doPostCom && !string.IsNullOrWhiteSpace(exp.PostCommand)) + { + script += exp.PostCommand + Environment.NewLine; + } + } + } + + return script; + } + + private static string DoSampleEnvironment(Experiment exp) + { + string script = ""; + + // Set Temps first + if (!string.IsNullOrWhiteSpace(exp.Temperature1)) + { + script += "CSET TEMP=" + exp.Temperature1 + Environment.NewLine; + } + + if (!string.IsNullOrWhiteSpace(exp.Temperature2)) + { + script += "CSET TEMP2=" + exp.Temperature2 + Environment.NewLine; + } + + // Check for pre-command - always run immediately after the temp is set + if (!string.IsNullOrWhiteSpace(exp.PreCommand)) + { + script += exp.PreCommand + Environment.NewLine; + } + + if (!string.IsNullOrWhiteSpace(exp.Field)) + { + script += "CSET FIELD=" + exp.Field + Environment.NewLine; + } + + if (!string.IsNullOrWhiteSpace(exp.ShearRate1)) + { + script += "CSET SHEAR_RATE_1=" + exp.ShearRate1 + Environment.NewLine; + } + + if (!string.IsNullOrWhiteSpace(exp.ShearRate2)) + { + script += "CSET SHEAR_RATE_2=" + exp.ShearRate2 + Environment.NewLine; + } + + if (!string.IsNullOrWhiteSpace(exp.ShearAngle1)) + { + script += "CSET SHEAR_ANGLE_1=" + exp.ShearAngle1 + Environment.NewLine; + } + + if (!string.IsNullOrWhiteSpace(exp.ShearAngle2)) + { + script += "CSET SHEAR_ANGLE_2=" + exp.ShearAngle2 + Environment.NewLine; + } + + return script; + } + + private static string CreateMoveCommand(bool isTrans, Experiment exp, ExperimentSettings settings) + { + string script = "MOVE pos=\""+exp.Position+"\""; + string fulltitle = ""; + + + if (!string.IsNullOrWhiteSpace(exp.Sample)) + { + fulltitle = "{" + exp.Sample + "}"; + } + + if (!string.IsNullOrWhiteSpace(exp.Background)) + { + fulltitle += "_{"+exp.Background+"}"; + } + + if (!string.IsNullOrWhiteSpace(exp.Thickness)) + { + script += " thick=" + exp.Thickness; + } + else + { + script += " thick=1"; + } + + if (!string.IsNullOrWhiteSpace(exp.Period)) + { + script += " period=" + exp.Period; + } + + if (isTrans) + { + script += WriteWaitFor(exp.TransWait, exp.Trans); + + if (!string.IsNullOrWhiteSpace(fulltitle)) + { + script += string.Format(" title=\"{0}_TRANS\"", "{"+exp.Sample+"}"); + } + } + else + { + script += WriteWaitFor(exp.SansWait, exp.Sans); + + if (!string.IsNullOrWhiteSpace(fulltitle)) + { + if (settings.CollectionMode == DataCollection.TRANSSANS_Events || settings.CollectionMode == DataCollection.TRANSSANS_Histogram) + { + script += string.Format(" title=\"{0}_TRANS_SANS\"", fulltitle); + } + else + { + script += string.Format(" title=\"{0}_SANS\"", fulltitle); + } + } + } + + return script + Environment.NewLine; + + } + + private static string WriteWaitFor(string waitfor, double length) + { + string script = ""; + + if (!string.IsNullOrWhiteSpace(waitfor)) + { + if (waitfor.ToLower() == WaitForUnits.MicroAmps.ToLower()) + { + script += " uAhr=" + length; + } + else if (waitfor.ToLower() == WaitForUnits.Minutes.ToLower()) + { + script += " min=" + length; + } + else if (waitfor.ToLower() == WaitForUnits.Seconds.ToLower()) + { + script += " seconds=" + length; + } + else if (waitfor.ToLower() == WaitForUnits.Frames.ToLower()) + { + script += " frame=" + length; + } + else + { + // If in doubt use micro-amps + script += " uAhr=" + length; + } + } + else + { + // If in doubt use micro-amps + script += " uAhr=" + length; + } + return script; + } + + public string EstimateRunTime(ObservableCollection runs, ExperimentSettings settings, Estimation estimation) + { + double minutes = 0; + + if (settings.NumSans > 0) + { + minutes += CalculateTime(runs, true, estimation.CountRate, estimation.MoveTime) * settings.NumSans; + } + + if (settings.NumTrans > 0) + { + minutes += CalculateTime(runs, false, estimation.CountRate, estimation.MoveTime) * settings.NumTrans; + } + + DateTime time = new DateTime(1, 1, 1, 0, 0, 0); + + time = time.AddMinutes(minutes); + + return time.ToLongTimeString(); + } + + private static double CalculateTime(ObservableCollection runs, bool isSans, double countRate, double secondsBetween) + { + double minutes = 0; + + foreach (Experiment exp in runs) + { + if (!string.IsNullOrWhiteSpace(exp.Position)) + { + if (isSans) + { + minutes += GetMinutes(exp.SansWait, exp.Sans, countRate); + } + else + { + minutes += GetMinutes(exp.TransWait, exp.Trans, countRate); + } + + minutes += secondsBetween / 60; + } + } + + return minutes; + } + + private static double GetMinutes(string wait, double length, double countRate) + { + if (wait == WaitForUnits.Minutes) + { + return length; + } + else if (wait == WaitForUnits.Seconds) + { + return length / 60.0; + } + else if (wait == WaitForUnits.Frames) + { + return length / (25.0 * 60.0); + } + else + { + // If in doubt use micro-amps + return length / (countRate / 60.0); + } + } + + } +} diff --git a/Scripting/OpenGenieScriptGenerator.py b/Scripting/OpenGenieScriptGenerator.py new file mode 100644 index 0000000..f8329ef --- /dev/null +++ b/Scripting/OpenGenieScriptGenerator.py @@ -0,0 +1,309 @@ +from datetime import datetime, timedelta +from Models.Estimation import Estimation +from Models.Experiment import Experiment +from Models.ExperimentSettings import ExperimentSettings + +class OpenGenieScriptGenerator: + def __init__(self, is_sans2d): + self.is_sans2d = is_sans2d + + def generate_script(self, runs, settings, scriptname): + script = f"# Script created by LOQ Script at {datetime.now()}\n" + script += "# Script MUST be loaded in to Open GENIE using the loadscript command.\n\n" + + if self.is_sans2d: + script += "QUALIFIERS /test\n" + + script += f"LOCAL transCount = 0 sansCount = 0 numTRANS={settings.num_trans} numSANS={settings.num_sans}\n\n" + script += "SETSCRIPTNAME THIS_PROCEDURE()\n\n" + script += f'Sample_par width={settings.sample_width} height={settings.sample_height} geometry="{settings.sample_geometry}"\n\n' + + if self.is_sans2d: + position_list = ",".join([exp.position for exp in runs if exp.position]) + if position_list: + script += f'IF NOT CHECK_MOVE_POS("{position_list}")\n' + script += ' PRINTEN "Invalid positions - script aborted"\n' + script += ' RETURN\n' + script += 'ENDIF\n\n' + script += "IF test\n" + script += " RETURN\n" + script += "ENDIF\n\n" + + if settings.order == "AllTrans": + if settings.loop_per_run: + script += self.do_all_trans_loop_each(runs, settings, True, True) + script += self.do_all_sans_loop_each(runs, settings, True, True) + else: + script += "LOOP\n\n" + script += self.do_all_trans(runs, settings, True, True) + script += self.do_all_sans(runs, settings, True, True) + script += "EXITIF ((transCount >= numTRANS) AND (sansCount >= numSANS))\n\n" + script += "ENDLOOP\n\n" + elif settings.order == "TransFirst": + if settings.loop_per_run: + for exp in runs: + script += self.do_single_trans_loop_each(exp, settings, True, False) + script += self.do_single_sans_loop_each(exp, settings, False, True) + else: + script += "LOOP\n\n" + for exp in runs: + script += self.do_single_trans(exp, settings, True, False) + script += self.do_single_sans(exp, settings, False, True) + script += "transCount = transCount + 1\n" + script += "sansCount = sansCount + 1\n\n" + script += "EXITIF ((transCount >= numTRANS) AND (sansCount >= numSANS))\n\n" + script += "ENDLOOP\n\n" + elif settings.order == "AllSans": + if settings.loop_per_run: + script += self.do_all_sans_loop_each(runs, settings, True, True) + script += self.do_all_trans_loop_each(runs, settings, True, True) + else: + script += "LOOP\n\n" + script += self.do_all_sans(runs, settings, True, True) + script += self.do_all_trans(runs, settings, True, True) + script += "EXITIF ((transCount >= numTRANS) AND (sansCount >= numSANS))\n\n" + script += "ENDLOOP\n\n" + elif settings.order == "SansFirst": + if settings.loop_per_run: + for exp in runs: + script += self.do_single_sans_loop_each(exp, settings, True, False) + script += self.do_single_trans_loop_each(exp, settings, False, True) + else: + script += "LOOP\n\n" + for exp in runs: + script += self.do_single_sans(exp, settings, True, False) + script += self.do_single_trans(exp, settings, False, True) + script += "transCount = transCount + 1\n" + script += "sansCount = sansCount + 1\n\n" + script += "EXITIF ((transCount >= numTRANS) AND (sansCount >= numSANS))\n\n" + script += "ENDLOOP\n\n" + + script += 'PRINTN "SCRIPT COMPLETED "\n\n' + return script + + def do_single_sans(self, exp, settings, do_pre_com, do_post_com): + script = "" + if settings.num_sans > 0 and exp.sans > 0: + script += "IF (sansCount < numSANS)\n" + script += self.set_sans_aperture(settings.a2_setting_sans) + script += self.do_single_run(False, exp, False, do_pre_com, do_post_com, settings) + script += "\nENDIF\n\n" + return script + + def do_single_trans(self, exp, settings, do_pre_com, do_post_com): + script = "" + if settings.num_trans > 0 and exp.trans > 0: + script += "IF (transCount < numTRANS)\n" + script += self.set_trans_aperture(settings.a2_setting_trans) + script += self.do_single_run(True, exp, False, do_pre_com, do_post_com, settings) + script += "\nENDIF\n\n" + return script + + def do_single_sans_loop_each(self, exp, settings, do_pre_com, do_post_com): + script = "" + if settings.num_sans > 0 and exp.sans > 0: + script += self.set_sans_aperture(settings.a2_setting_sans) + script += self.do_single_run(False, exp, True, do_pre_com, do_post_com, settings) + "\n" + return script + + def do_single_trans_loop_each(self, exp, settings, do_pre_com, do_post_com): + script = "" + if settings.num_trans > 0 and exp.trans > 0: + script += self.set_trans_aperture(settings.a2_setting_trans) + script += self.do_single_run(True, exp, True, do_pre_com, do_post_com, settings) + "\n" + return script + + def do_all_sans(self, runs, settings, do_pre_com, do_post_com): + script = "" + if settings.num_sans > 0: + script += self.set_sans_aperture(settings.a2_setting_sans) + script += "IF sansCount < numSANS\n\n" + for exp in runs: + script += self.do_single_run(False, exp, False, do_pre_com, do_post_com, settings) + "\n" + script += "sansCount = sansCount + 1\n\n" + script += "ENDIF\n\n" + return script + + def do_all_trans(self, runs, settings, do_pre_com, do_post_com): + script = "" + if settings.num_trans > 0: + script += self.set_trans_aperture(settings.a2_setting_trans) + script += "IF transCount < numTRANS\n\n" + for exp in runs: + script += self.do_single_run(True, exp, False, do_pre_com, do_post_com, settings) + "\n" + script += "transCount = transCount + 1\n\n" + script += "ENDIF\n\n" + return script + + def do_all_sans_loop_each(self, runs, settings, do_pre_com, do_post_com): + script = "" + if settings.num_sans > 0: + script += self.set_sans_aperture(settings.a2_setting_sans) + for exp in runs: + script += self.do_single_run(False, exp, True, do_pre_com, do_post_com, settings) + "\n" + return script + + def do_all_trans_loop_each(self, runs, settings, do_pre_com, do_post_com): + script = "" + if settings.num_trans > 0: + script += self.set_trans_aperture(settings.a2_setting_trans) + for exp in runs: + script += self.do_single_run(True, exp, True, do_pre_com, do_post_com, settings) + "\n" + return script + + def set_sans_aperture(self, a2_size_sans): + script = "" + if a2_size_sans == "Large": + script += "DO_SANS /LARGE\n\n" + elif a2_size_sans == "Medium": + script += "DO_SANS /MEDIUM\n\n" + elif a2_size_sans == "Small": + script += "DO_SANS /SMALL\n\n" + elif a2_size_sans == "Trans": + script += "DO_SANS /TRANS\n\n" + return script + + def set_trans_aperture(self, a2_size_trans): + script = "" + if not a2_size_trans: + script += "DO_TRANS\n\n" + else: + if a2_size_trans == "Large": + script += "DO_TRANS /LARGE\n\n" + elif a2_size_trans == "Medium": + script += "DO_TRANS /MEDIUM\n\n" + elif a2_size_trans == "Small": + script += "DO_TRANS /SMALL\n\n" + elif a2_size_trans == "Trans": + script += "DO_TRANS /TRANS\n\n" + return script + + def do_single_run(self, is_trans, exp, is_loop, do_pre_com, do_post_com, settings): + script = "" + if exp.trans > 0 or exp.sans > 0: + if exp.rb_number: + script += f"change rbno={exp.rb_number}\n" + if exp.position: + if do_pre_com: + script += self.do_sample_environment(exp) + if is_loop: + if is_trans: + script += "LOOP transCount FROM 1 TO numTRANS\n" + else: + script += "LOOP sansCount FROM 1 TO numSANS\n" + script += self.create_move_command(is_trans, exp, settings) + script += "ENDLOOP\n" + else: + script += self.create_move_command(is_trans, exp, settings) + if do_post_com and exp.post_command: + script += exp.post_command + "\n" + return script + + def do_sample_environment(self, exp): + script = "" + if exp.temperature1: + script += f"CSET TEMP={exp.temperature1}\n" + if exp.temperature2: + script += f"CSET TEMP2={exp.temperature2}\n" + if exp.pre_command: + script += exp.pre_command + "\n" + if exp.field: + script += f"CSET FIELD={exp.field}\n" + if exp.shear_rate1: + script += f"CSET SHEAR_RATE_1={exp.shear_rate1}\n" + if exp.shear_rate2: + script += f"CSET SHEAR_RATE_2={exp.shear_rate2}\n" + if exp.shear_angle1: + script += f"CSET SHEAR_ANGLE_1={exp.shear_angle1}\n" + if exp.shear_angle2: + script += f"CSET SHEAR_ANGLE_2={exp.shear_angle2}\n" + return script + + def create_move_command(self, is_trans, exp, settings): + script = f'MOVE pos="{exp.position}"' + fulltitle = f"{{{exp.sample}}}" + if exp.background: + fulltitle += f"_{{{exp.background}}}" + if exp.thickness: + script += f" thick={exp.thickness}" + else: + script += " thick=1" + if exp.period: + script += f" period={exp.period}" + if is_trans: + script += self.write_wait_for(exp.trans_wait, exp.trans) + if fulltitle: + script += f' title="{fulltitle}_TRANS"' + else: + script += self.write_wait_for(exp.sans_wait, exp.sans) + if fulltitle: + if settings.collection_mode in ["TRANSSANS_Events", "TRANSSANS_Histogram"]: + script += f' title="{fulltitle}_TRANS_SANS"' + else: + script += f' title="{fulltitle}_SANS"' + return script + "\n" + + def write_wait_for(self, waitfor, length): + script = "" + if waitfor: + if waitfor.lower() == "MicroAmps".lower(): + script += f" uAhr={length}" + elif waitfor.lower() == "Minutes".lower(): + script += f" min={length}" + elif waitfor.lower() == "Seconds".lower(): + script += f" seconds={length}" + elif waitfor.lower() == "Frames".lower(): + script += f" frame={length}" + else: + script += f" uAhr={length}" + else: + script += f" uAhr={length}" + return script + + def estimate_run_time(self, runs, settings, estimation): + minutes = 0 + if settings.num_sans > 0: + minutes += self.calculate_time(runs, True, estimation.count_rate, estimation.move_time) * settings.num_sans + if settings.num_trans > 0: + minutes += self.calculate_time(runs, False, estimation.count_rate, estimation.move_time) * settings.num_trans + time = datetime(1, 1, 1, 0, 0, 0) + timedelta(minutes=minutes) + return time.strftime("%H:%M:%S") + + @staticmethod + def calculate_time(runs, is_sans, count_rate, seconds_between): + minutes = 0 + for exp in runs: + if exp.position: + if is_sans: + minutes += OpenGenieScriptGenerator.get_minutes(exp.sans_wait, exp.sans, count_rate) + else: + minutes += OpenGenieScriptGenerator.get_minutes(exp.trans_wait, exp.trans, count_rate) + minutes += seconds_between / 60 + return minutes + + @staticmethod + def get_minutes(wait, length, count_rate): + if wait == "Minutes": + return length + elif wait == "Seconds": + return length / 60.0 + elif wait == "Frames": + return length / (25.0 * 60.0) + else: + return length / (count_rate / 60.0) + + + + + + + +if __name__ == "__main__": + runs = [Experiment(position="pos1", trans=10, sans=20, trans_wait="Minutes", sans_wait="Minutes", sample="Sample1", thickness=1.0)] + settings = ExperimentSettings(sample_width="10", sample_height="10", sample_geometry="Flat", order="AllTrans", num_trans=1, num_sans=1, collection_mode="Events", a2_setting_trans="Large", a2_setting_sans="Large", loop_per_run=False) + estimation = Estimation(count_rate=100, move_time=10) + generator = OpenGenieScriptGenerator(is_sans2d=True) + script = generator.generate_script(runs, settings, "my_script") + print(script) + estimated_time = generator.estimate_run_time(runs, settings, estimation) + print(f"Estimated run time: {estimated_time}") \ No newline at end of file diff --git a/SettingsPanel.py b/SettingsPanel.py new file mode 100644 index 0000000..2938fed --- /dev/null +++ b/SettingsPanel.py @@ -0,0 +1,98 @@ +import tkinter as tk +from tkinter import ttk +from ViewModels.SettingsPanelVM import SettingsPanelVM + + +class SettingsPanel(ttk.Frame): + def __init__(self, parent, settings_vm=None): + super().__init__(parent) + + self.settings_vm = settings_vm if settings_vm is not None else SettingsPanelVM() + self.on_preview_clicked = None + self.on_write_clicked = None + + self.create_widgets() + + def create_widgets(self): + #self.preview_button = ttk.Button(self, text="Preview Script", command=self.on_preview_clicked) + #self.preview_button.pack(side=tk.LEFT, padx=5, pady=5) + #self.write_button = ttk.Button(self, text="Write Script", command=self.on_write_clicked) + #self.write_button.pack(side=tk.LEFT, padx=5, pady=5) + + + label = ttk.Label(self, text="Settings", font=("Helvetica", 16, "bold")) + label.pack(pady=10) + + grid_frame = ttk.Frame(self) + grid_frame.pack(padx=10, pady=10) + + # Order + ttk.Label(grid_frame, text="Order:").grid(row=0, column=0, sticky=tk.E) + self.com_order = ttk.Combobox(grid_frame, values=self.settings_vm.order_choices) + self.com_order.grid(row=0, column=1, columnspan=2, padx=5, pady=5) + + # Do SANS + ttk.Label(grid_frame, text="Do SANS:").grid(row=1, column=0, sticky=tk.E) + sans_frame = ttk.Frame(grid_frame) + sans_frame.grid(row=1, column=1, columnspan=2) + self.txt_times_sans = ttk.Entry(sans_frame, width=5) + self.txt_times_sans.pack(side=tk.LEFT, padx=5, pady=5) + ttk.Label(sans_frame, text="time(s)").pack(side=tk.LEFT) + + # Do TRANS + ttk.Label(grid_frame, text="Do TRANS:").grid(row=2, column=0, sticky=tk.E) + trans_frame = ttk.Frame(grid_frame) + trans_frame.grid(row=2, column=1, columnspan=2) + self.txt_times_trans = ttk.Entry(trans_frame, width=5) + self.txt_times_trans.pack(side=tk.LEFT, padx=5, pady=5) + ttk.Label(trans_frame, text="time(s)").pack(side=tk.LEFT) + + # Loop over each run + self.chk_loop_run = ttk.Checkbutton(grid_frame, text="Loop over each run?") + self.chk_loop_run.grid(row=3, column=0, columnspan=3, pady=5) + + # Sample Geometry + ttk.Label(grid_frame, text="Sample Geometry:").grid(row=6, column=0, sticky=tk.E) + self.com_sample_geo = ttk.Combobox(grid_frame, values=self.settings_vm.geometry_choices) + self.com_sample_geo.grid(row=6, column=1, columnspan=2, padx=5, pady=5) + + # Sample Height + ttk.Label(grid_frame, text="Sample Height:").grid(row=7, column=0, sticky=tk.E) + sample_height_frame = ttk.Frame(grid_frame) + sample_height_frame.grid(row=7, column=1, columnspan=2) + self.txt_sample_height = ttk.Entry(sample_height_frame, width=5) + self.txt_sample_height.pack(side=tk.LEFT, padx=5, pady=5) + ttk.Label(sample_height_frame, text="mm").pack(side=tk.LEFT) + + # Sample Width + ttk.Label(grid_frame, text="Sample Width:").grid(row=8, column=0, sticky=tk.E) + sample_width_frame = ttk.Frame(grid_frame) + sample_width_frame.grid(row=8, column=1, columnspan=2) + self.txt_sample_width = ttk.Entry(sample_width_frame, width=5) + self.txt_sample_width.pack(side=tk.LEFT, padx=5, pady=5) + ttk.Label(sample_width_frame, text="mm").pack(side=tk.LEFT) + + # Buttons + button_frame = ttk.Frame(self) + button_frame.pack(pady=10) + self.btn_preview_script = ttk.Button(button_frame, text="Preview Script", command=self.btn_preview_script_click) + self.btn_preview_script.pack(side=tk.LEFT, padx=5) + self.btn_write_script = ttk.Button(button_frame, text="Write Script", command=self.btn_write_script_click) + self.btn_write_script.pack(side=tk.LEFT, padx=5) + + def btn_preview_script_click(self): + if self.on_preview_clicked: + self.on_preview_clicked() + + + + def btn_write_script_click(self): + if self.on_write_clicked: + self.on_write_clicked() + + +if __name__ == "__main__": + root = tk.Tk() + panel = SettingsPanel(root) + panel.pack() + root.mainloop() \ No newline at end of file diff --git a/SettingsPanel.xaml b/SettingsPanel.xaml new file mode 100644 index 0000000..8c63f8f --- /dev/null +++ b/SettingsPanel.xaml @@ -0,0 +1,72 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Loop over each run? + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/SettingsPanel.xaml.cs b/SettingsPanel.xaml.cs new file mode 100644 index 0000000..3e768e6 --- /dev/null +++ b/SettingsPanel.xaml.cs @@ -0,0 +1,47 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Imaging; +using System.Windows.Navigation; +using System.Windows.Shapes; + +namespace LOQ_Script_Gui +{ + /// + /// Interaction logic for SettingsPanel.xaml + /// + public partial class SettingsPanel : UserControl + { + public event EventHandler OnPreviewClicked; + public event EventHandler OnWriteClicked; + + public SettingsPanel() + { + InitializeComponent(); + } + + private void btnPreviewScript_Click(object sender, RoutedEventArgs e) + { + if (OnPreviewClicked != null) + { + OnPreviewClicked(sender, e); + } + } + + private void btnWriteScript_Click(object sender, RoutedEventArgs e) + { + if (OnWriteClicked != null) + { + OnWriteClicked(sender, e); + } + } + } +} diff --git a/Transcript_GitCopilot.txt b/Transcript_GitCopilot.txt new file mode 100644 index 0000000..17862f6 --- /dev/null +++ b/Transcript_GitCopilot.txt @@ -0,0 +1,3937 @@ +dehoni: @workspace /explain + +Workspace: The provided code is a C# file named + +SettingsPanel.xaml.cs + +, which is part of a WPF (Windows Presentation Foundation) application. This file defines the code-behind for a user control named + +SettingsPanel + +. The + +SettingsPanel + + class inherits from + +UserControl + +, which is a base class for defining custom user interface controls in WPF. + +The + +SettingsPanel + + class contains two public events: + +OnPreviewClicked + + and + +OnWriteClicked + +. These events are used to notify subscribers when the corresponding buttons are clicked. Events in C# are a way to provide notifications to other parts of the application when something of interest happens. + +The constructor + +SettingsPanel() + + initializes the component by calling the + +InitializeComponent() + + method. This method is automatically generated and is responsible for loading the XAML-defined UI elements and setting up the control. + +There are two private methods in the class: + +btnPreviewScript_Click + + and + +btnWriteScript_Click + +. These methods are event handlers for button click events. When the + +btnPreviewScript_Click + + method is called (which happens when the associated button is clicked), it checks if there are any subscribers to the + +OnPreviewClicked + + event. If there are, it raises the event by invoking the + +OnPreviewClicked + + delegate, passing the sender and event arguments. + +Similarly, the + +btnWriteScript_Click + + method handles the click event for another button. It checks for subscribers to the + +OnWriteClicked + + event and raises the event if there are any. + +Overall, this code sets up a user control with two buttons, each of which can trigger events that other parts of the application can subscribe to and handle accordingly. This allows for a decoupled design where the logic for handling button clicks can be separated from the user control itself. + +dehoni: Please translate the selected code into python and use approbriate packages. + +GitHub Copilot: Here is the translated code into Python using the `tkinter` package, which is a standard GUI toolkit in Python: + +```python + + +import tkinter as tk +from tkinter import ttk + +class SettingsPanel(ttk.Frame): + def __init__(self, parent): + super().__init__(parent) + self.on_preview_clicked = None + self.on_write_clicked = None + + self.preview_button = ttk.Button(self, text="Preview Script", command=self.btn_preview_script_click) + self.preview_button.pack() + + self.write_button = ttk.Button(self, text="Write Script", command=self.btn_write_script_click) + self.write_button.pack() + + def btn_preview_script_click(self): + if self.on_preview_clicked is not None: + self.on_preview_clicked() + + def btn_write_script_click(self): + if self.on_write_clicked is not None: + self.on_write_clicked() + +if __name__ == "__main__": + root = tk.Tk() + panel = SettingsPanel(root) + panel.pack() + root.mainloop() +``` + +In this Python version: +- The + +SettingsPanel + + class inherits from `ttk.Frame`. +- The `on_preview_clicked` and `on_write_clicked` attributes are used to store callback functions. +- The `btn_preview_script_click` and `btn_write_script_click` methods are defined to call these callbacks if they are set. +- The `ttk.Button` widgets are created for the preview and write actions, and their `command` attributes are set to the corresponding methods. +- The `if __name__ == "__main__":` block creates a simple application to demonstrate the + +SettingsPanel + + usage. + +dehoni: Please translate the selected code into python and use approbriate packages. + +GitHub Copilot: Here is the translated code into Python using the `tkinter` package: + +```python + + +import tkinter as tk +from tkinter import ttk + +class SettingsPanel(ttk.Frame): + def __init__(self, parent): + super().__init__(parent) + + self.settings_vm = SettingsPanelVM() + + self.create_widgets() + + def create_widgets(self): + label = ttk.Label(self, text="Settings", font=("Helvetica", 16, "bold")) + label.pack(pady=10) + + grid_frame = ttk.Frame(self) + grid_frame.pack(padx=10, pady=10) + + # Order + ttk.Label(grid_frame, text="Order:").grid(row=0, column=0, sticky=tk.E) + self.com_order = ttk.Combobox(grid_frame, values=self.settings_vm.order_choices) + self.com_order.grid(row=0, column=1, columnspan=2, padx=5, pady=5) + + # Do SANS + ttk.Label(grid_frame, text="Do SANS:").grid(row=1, column=0, sticky=tk.E) + sans_frame = ttk.Frame(grid_frame) + sans_frame.grid(row=1, column=1, columnspan=2) + self.txt_times_sans = ttk.Entry(sans_frame, width=5) + self.txt_times_sans.pack(side=tk.LEFT, padx=5, pady=5) + ttk.Label(sans_frame, text="time(s)").pack(side=tk.LEFT) + + # Do TRANS + ttk.Label(grid_frame, text="Do TRANS:").grid(row=2, column=0, sticky=tk.E) + trans_frame = ttk.Frame(grid_frame) + trans_frame.grid(row=2, column=1, columnspan=2) + self.txt_times_trans = ttk.Entry(trans_frame, width=5) + self.txt_times_trans.pack(side=tk.LEFT, padx=5, pady=5) + ttk.Label(trans_frame, text="time(s)").pack(side=tk.LEFT) + + # Loop over each run + self.chk_loop_run = ttk.Checkbutton(grid_frame, text="Loop over each run?") + self.chk_loop_run.grid(row=3, column=0, columnspan=3, pady=5) + + # Sample Geometry + ttk.Label(grid_frame, text="Sample Geometry:").grid(row=6, column=0, sticky=tk.E) + self.com_sample_geo = ttk.Combobox(grid_frame, values=self.settings_vm.geometry_choices) + self.com_sample_geo.grid(row=6, column=1, columnspan=2, padx=5, pady=5) + + # Sample Height + ttk.Label(grid_frame, text="Sample Height:").grid(row=7, column=0, sticky=tk.E) + sample_height_frame = ttk.Frame(grid_frame) + sample_height_frame.grid(row=7, column=1, columnspan=2) + self.txt_sample_height = ttk.Entry(sample_height_frame, width=5) + self.txt_sample_height.pack(side=tk.LEFT, padx=5, pady=5) + ttk.Label(sample_height_frame, text="mm").pack(side=tk.LEFT) + + # Sample Width + ttk.Label(grid_frame, text="Sample Width:").grid(row=8, column=0, sticky=tk.E) + sample_width_frame = ttk.Frame(grid_frame) + sample_width_frame.grid(row=8, column=1, columnspan=2) + self.txt_sample_width = ttk.Entry(sample_width_frame, width=5) + self.txt_sample_width.pack(side=tk.LEFT, padx=5, pady=5) + ttk.Label(sample_width_frame, text="mm").pack(side=tk.LEFT) + + # Buttons + button_frame = ttk.Frame(self) + button_frame.pack(pady=10) + self.btn_preview_script = ttk.Button(button_frame, text="Preview Script", command=self.btn_preview_script_click) + self.btn_preview_script.pack(side=tk.LEFT, padx=5) + self.btn_write_script = ttk.Button(button_frame, text="Write Script", command=self.btn_write_script_click) + self.btn_write_script.pack(side=tk.LEFT, padx=5) + + def btn_preview_script_click(self): + if self.on_preview_clicked is not None: + self.on_preview_clicked() + + def btn_write_script_click(self): + if self.on_write_clicked is not None: + self.on_write_clicked() + +class SettingsPanelVM: + def __init__(self): + self.order_choices = ["Choice1", "Choice2", "Choice3"] + self.geometry_choices = ["Geometry1", "Geometry2", "Geometry3"] + +if __name__ == "__main__": + root = tk.Tk() + panel = SettingsPanel(root) + panel.pack() + root.mainloop() +``` + +In this Python version: +- The `SettingsPanel` class inherits from `ttk.Frame`. +- The `SettingsPanelVM` class is a simple ViewModel to provide choices for the comboboxes. +- The `create_widgets` method sets up the UI elements similar to the XAML layout. +- The `btn_preview_script_click` and `btn_write_script_click` methods handle button clicks. +- The `if __name__ == "__main__":` block creates a simple application to demonstrate the `SettingsPanel` usage. + +dehoni: Please translate the selected code into python and use approbriate packages. + +GitHub Copilot: The provided XML file is a project user file for a Visual Studio project. In Python, we can use the `xml.etree.ElementTree` module to create a similar XML structure. Here is the translated code: + +```python + + +import xml.etree.ElementTree as ET + +root = ET.Element("Project", ToolsVersion="14.0", xmlns="http://schemas.microsoft.com/developer/msbuild/2003") + +property_group = ET.SubElement(root, "PropertyGroup") +ET.SubElement(property_group, "PublishUrlHistory").text = "publish\\" +ET.SubElement(property_group, "InstallUrlHistory") +ET.SubElement(property_group, "SupportUrlHistory") +ET.SubElement(property_group, "UpdateUrlHistory") +ET.SubElement(property_group, "BootstrapperUrlHistory") +ET.SubElement(property_group, "ErrorReportUrlHistory") +ET.SubElement(property_group, "FallbackCulture").text = "en-US" +ET.SubElement(property_group, "VerifyUploadedFiles").text = "false" + +tree = ET.ElementTree(root) +tree.write("SANS_Script_GUI_v4.xml", encoding="utf-8", xml_declaration=True) +``` + +This Python script uses the `xml.etree.ElementTree` module to create an XML structure similar to the provided `.csproj.user` file. The resulting XML file will be saved as `SANS_Script_GUI_v4.xml`. + +dehoni: Please translate the selected code into python and use approbriate packages. + +GitHub Copilot: Here is the translated code into Python using the `xml.etree.ElementTree` module: + +```python + + +import xml.etree.ElementTree as ET + +root = ET.Element("Project", ToolsVersion="12.0", DefaultTargets="Build", xmlns="http://schemas.microsoft.com/developer/msbuild/2003") + +ET.SubElement(root, "Import", Project="$(MSBuildExtensionsPath)\\$(MSBuildToolsVersion)\\Microsoft.Common.props", Condition="Exists('$(MSBuildExtensionsPath)\\$(MSBuildToolsVersion)\\Microsoft.Common.props')") + +property_group = ET.SubElement(root, "PropertyGroup") +ET.SubElement(property_group, "Configuration", Condition=" '$(Configuration)' == '' ").text = "Debug" +ET.SubElement(property_group, "Platform", Condition=" '$(Platform)' == '' ").text = "AnyCPU" +ET.SubElement(property_group, "ProjectGuid").text = "{5DBFF2C3-05A0-4B4B-9080-8224D86A7B40}" +ET.SubElement(property_group, "OutputType").text = "WinExe" +ET.SubElement(property_group, "AppDesignerFolder").text = "Properties" +ET.SubElement(property_group, "RootNamespace").text = "LOQ_Script_Gui" +ET.SubElement(property_group, "AssemblyName").text = "SANS_Script_Gui_v4" +ET.SubElement(property_group, "TargetFrameworkVersion").text = "v4.8" +ET.SubElement(property_group, "FileAlignment").text = "512" +ET.SubElement(property_group, "ProjectTypeGuids").text = "{60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}" +ET.SubElement(property_group, "WarningLevel").text = "4" +ET.SubElement(property_group, "IsWebBootstrapper").text = "false" +ET.SubElement(property_group, "TargetFrameworkProfile") +ET.SubElement(property_group, "PublishUrl").text = "publish\\" +ET.SubElement(property_group, "Install").text = "true" +ET.SubElement(property_group, "InstallFrom").text = "Disk" +ET.SubElement(property_group, "UpdateEnabled").text = "false" +ET.SubElement(property_group, "UpdateMode").text = "Foreground" +ET.SubElement(property_group, "UpdateInterval").text = "7" +ET.SubElement(property_group, "UpdateIntervalUnits").text = "Days" +ET.SubElement(property_group, "UpdatePeriodically").text = "false" +ET.SubElement(property_group, "UpdateRequired").text = "false" +ET.SubElement(property_group, "MapFileExtensions").text = "true" +ET.SubElement(property_group, "ApplicationRevision").text = "0" +ET.SubElement(property_group, "ApplicationVersion").text = "1.0.0.%2a" +ET.SubElement(property_group, "UseApplicationTrust").text = "false" +ET.SubElement(property_group, "BootstrapperEnabled").text = "true" + +debug_property_group = ET.SubElement(root, "PropertyGroup", Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ") +ET.SubElement(debug_property_group, "PlatformTarget").text = "x64" +ET.SubElement(debug_property_group, "DebugSymbols").text = "true" +ET.SubElement(debug_property_group, "DebugType").text = "full" +ET.SubElement(debug_property_group, "Optimize").text = "false" +ET.SubElement(debug_property_group, "OutputPath").text = "bin\\Debug\\" +ET.SubElement(debug_property_group, "DefineConstants").text = "DEBUG;TRACE" +ET.SubElement(debug_property_group, "ErrorReport").text = "prompt" +ET.SubElement(debug_property_group, "WarningLevel").text = "4" +ET.SubElement(debug_property_group, "Prefer32Bit").text = "false" + +release_property_group = ET.SubElement(root, "PropertyGroup", Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ") +ET.SubElement(release_property_group, "PlatformTarget").text = "x64" +ET.SubElement(release_property_group, "DebugType").text = "pdbonly" +ET.SubElement(release_property_group, "Optimize").text = "true" +ET.SubElement(release_property_group, "OutputPath").text = "bin\\Release\\" +ET.SubElement(release_property_group, "DefineConstants").text = "TRACE" +ET.SubElement(release_property_group, "ErrorReport").text = "prompt" +ET.SubElement(release_property_group, "WarningLevel").text = "4" + +icon_property_group = ET.SubElement(root, "PropertyGroup") +ET.SubElement(icon_property_group, "ApplicationIcon").text = "SANS_Icon.ico" + +references = [ + ("Microsoft.Office.Interop.Excel, Version=15.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c, processorArchitecture=MSIL", "True"), + "ReachFramework", + "System", + "System.Data", + "System.Xml", + "Microsoft.CSharp", + "System.Core", + "System.Xml.Linq", + "System.Data.DataSetExtensions", + ("System.Xaml", "4.0"), + "WindowsBase", + "PresentationCore", + "PresentationFramework" +] + +item_group = ET.SubElement(root, "ItemGroup") +for ref in references: + if isinstance(ref, tuple): + reference = ET.SubElement(item_group, "Reference", Include=ref[0]) + ET.SubElement(reference, "EmbedInteropTypes").text = ref[1] + else: + ET.SubElement(item_group, "Reference", Include=ref) + +compile_files = [ + "Models\\DataCollection.cs", + "Models\\InstrumentInfo.cs", + "IO\\CsvExporter.cs", + "Dialogs\\PreviewDialog.xaml.cs", + "IO\\ExcelIO.cs", + "Models\\A2Setting.cs", + "Models\\Estimation.cs", + "Models\\Geometry.cs", + "Models\\RunOrder.cs", + "EstimationPanel.xaml.cs", + "Models\\Experiment.cs", + "Models\\ExperimentSettings.cs", + "Models\\WaitForUnits.cs", + "Scripting\\GeniePythonScriptGenerator.cs", + "Scripting\\IScriptGenerator.cs", + "Scripting\\OpenGenieScriptGenerator.cs", + "SettingsPanel.xaml.cs", + "ViewModels\\DataGridVM.cs", + "ViewModels\\EstimationPanelVM.cs", + "ViewModels\\InstrumentVM.cs", + "ViewModels\\SettingsPanelVM.cs", + "App.xaml.cs", + "MainWindow.xaml.cs", + "Properties\\AssemblyInfo.cs", + "Properties\\Resources.Designer.cs", + "Properties\\Settings.Designer.cs" +] + +for file in compile_files: + compile = ET.SubElement(item_group, "Compile", Include=file) + if file.endswith(".xaml.cs"): + ET.SubElement(compile, "DependentUpon").text = file.replace(".cs", "") + elif file.endswith(".Designer.cs"): + ET.SubElement(compile, "AutoGen").text = "True" + ET.SubElement(compile, "DesignTime").text = "True" + ET.SubElement(compile, "DependentUpon").text = file.replace(".Designer.cs", ".resx") + if "Settings" in file: + ET.SubElement(compile, "DesignTimeSharedInput").text = "True" + +pages = [ + "Dialogs\\PreviewDialog.xaml", + "EstimationPanel.xaml", + "MainWindow.xaml", + "SettingsPanel.xaml" +] + +for page in pages: + page_element = ET.SubElement(item_group, "Page", Include=page) + ET.SubElement(page_element, "SubType").text = "Designer" + ET.SubElement(page_element, "Generator").text = "MSBuild:Compile" + +embedded_resources = [ + "Properties\\Resources.resx" +] + +for resource in embedded_resources: + embedded_resource = ET.SubElement(item_group, "EmbeddedResource", Include=resource) + ET.SubElement(embedded_resource, "Generator").text = "ResXFileCodeGenerator" + ET.SubElement(embedded_resource, "LastGenOutput").text = resource.replace(".resx", ".Designer.cs") + +none_files = [ + "Properties\\Settings.settings", + "App.config" +] + +for none_file in none_files: + none = ET.SubElement(item_group, "None", Include=none_file) + if none_file.endswith(".settings"): + ET.SubElement(none, "Generator").text = "SettingsSingleFileGenerator" + ET.SubElement(none, "LastGenOutput").text = none_file.replace(".settings", ".Designer.cs") + +ET.SubElement(item_group, "AppDesigner", Include="Properties\\") + +bootstrapper_packages = [ + (".NETFramework,Version=v4.5", "Microsoft .NET Framework 4.5 %28x86 and x64%29", "true"), + ("Microsoft.Net.Framework.3.5.SP1", ".NET Framework 3.5 SP1", "false") +] + +for package in bootstrapper_packages: + bootstrapper_package = ET.SubElement(item_group, "BootstrapperPackage", Include=package[0]) + ET.SubElement(bootstrapper_package, "Visible").text = "False" + ET.SubElement(bootstrapper_package, "ProductName").text = package[1] + ET.SubElement(bootstrapper_package, "Install").text = package[2] + +ET.SubElement(item_group, "Resource", Include="SANS_Icon.ico") + +ET.SubElement(item_group, "WCFMetadata", Include="Connected Services\\") + +ET.SubElement(root, "Import", Project="$(MSBuildToolsPath)\\Microsoft.CSharp.targets") + +tree = ET.ElementTree(root) +tree.write("SANS_Script_GUI_v4.xml", encoding="utf-8", xml_declaration=True) +``` + +This Python script uses the `xml.etree.ElementTree` module to create an XML structure similar to the provided `.csproj` file. The resulting XML file will be saved as `SANS_Script_GUI_v4.xml`. + +dehoni: Please translate the selected code into python and use approbriate packages. + +GitHub Copilot: Here is the translated code into Python using the `tkinter` package: + +```python + + +import tkinter as tk +from tkinter import ttk, filedialog, messagebox +from tkinter.scrolledtext import ScrolledText + +class MainWindow(tk.Tk): + def __init__(self): + super().__init__() + + self.title("SANS Script GUI") + self.geometry("800x600") + + self.data = DataGridVM() + self.inst_info = InstrumentInfo() + self.settings = SettingsPanelVM(self.inst_info) + self.estimate = Estimation() + self.instrument = InstrumentVM(self.settings.experiment, self.estimate) + + self.create_widgets() + self.load_last_instrument() + + def create_widgets(self): + self.data_grid = ttk.Treeview(self) + self.data_grid.pack(fill=tk.BOTH, expand=True) + + self.pnl_settings = SettingsPanel(self, self.settings) + self.pnl_settings.pack(fill=tk.X) + self.pnl_settings.on_preview_clicked = self.pnl_settings_on_preview_clicked + self.pnl_settings.on_write_clicked = self.pnl_settings_on_write_clicked + + self.pnl_estimate = EstimationPanel(self, self.estimate) + self.pnl_estimate.pack(fill=tk.X) + self.pnl_estimate.on_calculate_clicked = self.pnl_estimate_on_calculate_clicked + + self.btn_create_new_table = ttk.Button(self, text="Create New Table", command=self.btn_create_new_table_click) + self.btn_create_new_table.pack(side=tk.LEFT, padx=5, pady=5) + + self.btn_open_excel = ttk.Button(self, text="Open Excel", command=self.btn_open_excel_click) + self.btn_open_excel.pack(side=tk.LEFT, padx=5, pady=5) + + self.mnu_main = tk.Menu(self) + self.config(menu=self.mnu_main) + + file_menu = tk.Menu(self.mnu_main, tearoff=0) + self.mnu_main.add_cascade(label="File", menu=file_menu) + file_menu.add_command(label="Export to Excel", command=self.mnu_export_to_excel_click) + file_menu.add_command(label="Save as CSV", command=self.mnu_save_csv_click) + file_menu.add_separator() + file_menu.add_command(label="Exit", command=self.mnu_exit_click) + + def load_last_instrument(self): + inst = "LARMOR" # Replace with actual settings retrieval + if inst == "LARMOR": + self.instrument.is_larmor = True + elif inst == "LOQ": + self.instrument.is_loq = True + elif inst == "SANS2D": + self.instrument.is_sans2d = True + elif inst == "ZOOM": + self.instrument.is_zoom = True + else: + self.instrument.is_larmor = True + + def pnl_settings_on_write_clicked(self): + filetypes = [("genie_python Files", "*.py")] if self.instrument.is_larmor or self.instrument.is_loq or self.instrument.is_zoom or self.instrument.is_sans2d else [("Open Genie Files", "*.gcl")] + filepath = filedialog.asksaveasfilename(defaultextension=filetypes[0][1], filetypes=filetypes) + if filepath: + scriptname = filepath.split('/')[-1].replace(" ", "_").rsplit('.', 1)[0] + script = self.inst_info.script_generator.generate_script(self.data.runs, self.settings.experiment, scriptname) + with open(filepath, 'w') as file: + file.write(script) + + def pnl_settings_on_preview_clicked(self): + script = self.inst_info.script_generator.generate_script(self.data.runs, self.settings.experiment, "my_script") + preview_dialog = PreviewDialog(self, script) + preview_dialog.show() + + def pnl_estimate_on_calculate_clicked(self): + self.estimate.script_time = self.inst_info.script_generator.estimate_run_time(self.data.runs, self.settings.experiment, self.estimate) + + def btn_create_new_table_click(self): + self.data.runs.clear() + + def btn_open_excel_click(self): + filepath = filedialog.askopenfilename(defaultextension=".xlsx", filetypes=[("Excel Files", "*.xls;*.xlsx")]) + if filepath: + try: + excel = ExcelIO() + excel.open_excel_workbook(filepath) + exps = excel.extract_experiments() + self.data.runs = exps + except Exception as err: + messagebox.showerror("SANS Script - Error", "Could not open Excel spreadsheet") + + def mnu_exit_click(self): + self.destroy() + + def mnu_export_to_excel_click(self): + excel = ExcelIO() + excel.export_data_to_excel(self.data.runs) + + def mnu_save_csv_click(self): + filepath = filedialog.asksaveasfilename(defaultextension=".csv", filetypes=[("CSV File", "*.csv")]) + if filepath: + try: + CsvExporter.export(filepath, self.data.runs) + except Exception as err: + messagebox.showerror("SANS Script - Error", "Could not save as CSV") + + def on_close(self): + if self.instrument.is_larmor: + last_instrument = "LARMOR" + elif self.instrument.is_loq: + last_instrument = "LOQ" + elif self.instrument.is_sans2d: + last_instrument = "SANS2D" + elif self.instrument.is_zoom: + last_instrument = "ZOOM" + else: + last_instrument = "LARMOR" + # Save last_instrument to settings + self.destroy() + +class SettingsPanel(ttk.Frame): + def __init__(self, parent, settings_vm): + super().__init__(parent) + self.settings_vm = settings_vm + self.on_preview_clicked = None + self.on_write_clicked = None + self.create_widgets() + + def create_widgets(self): + self.preview_button = ttk.Button(self, text="Preview Script", command=self.preview_click) + self.preview_button.pack(side=tk.LEFT, padx=5, pady=5) + self.write_button = ttk.Button(self, text="Write Script", command=self.write_click) + self.write_button.pack(side=tk.LEFT, padx=5, pady=5) + + def preview_click(self): + if self.on_preview_clicked: + self.on_preview_clicked() + + def write_click(self): + if self.on_write_clicked: + self.on_write_clicked() + +class EstimationPanel(ttk.Frame): + def __init__(self, parent, estimate): + super().__init__(parent) + self.estimate = estimate + self.on_calculate_clicked = None + self.create_widgets() + + def create_widgets(self): + self.calculate_button = ttk.Button(self, text="Calculate", command=self.calculate_click) + self.calculate_button.pack(side=tk.LEFT, padx=5, pady=5) + + def calculate_click(self): + if self.on_calculate_clicked: + self.on_calculate_clicked() + +class PreviewDialog(tk.Toplevel): + def __init__(self, parent, script): + super().__init__(parent) + self.title("Preview Script") + self.geometry("600x400") + self.script_text = ScrolledText(self) + self.script_text.pack(fill=tk.BOTH, expand=True) + self.script_text.insert(tk.END, script) + self.script_text.config(state=tk.DISABLED) + + def show(self): + self.grab_set() + self.wait_window() + +class DataGridVM: + def __init__(self): + self.runs = [] + +class SettingsPanelVM: + def __init__(self, inst_info): + self.experiment = Experiment() + self.inst_info = inst_info + +class Estimation: + def __init__(self): + self.script_time = 0 + +class InstrumentInfo: + class ScriptGenerator: + @staticmethod + def generate_script(runs, experiment, scriptname): + return f"# Script for {scriptname}\n# Runs: {runs}\n# Experiment: {experiment}" + + @staticmethod + def estimate_run_time(runs, experiment, estimate): + return len(runs) * 10 # Dummy implementation + +class InstrumentVM: + def __init__(self, experiment, estimate): + self.is_larmor = False + self.is_loq = False + self.is_sans2d = False + self.is_zoom = False + self.experiment = experiment + self.estimate = estimate + +class ExcelIO: + def open_excel_workbook(self, filename): + pass # Dummy implementation + + def extract_experiments(self): + return [] # Dummy implementation + + def export_data_to_excel(self, runs): + pass # Dummy implementation + +class CsvExporter: + @staticmethod + def export(filename, runs): + pass # Dummy implementation + +class Experiment: + pass # Dummy implementation + +if __name__ == "__main__": + app = MainWindow() + app.protocol("WM_DELETE_WINDOW", app.on_close) + app.mainloop() +``` + +In this Python version: +- The + +MainWindow + + class inherits from `tk.Tk` and sets up the main window and its widgets. +- The `SettingsPanel` and `EstimationPanel` classes are custom frames for the settings and estimation panels. +- The + +PreviewDialog + + class is a custom dialog for previewing the script. +- The + +DataGridVM + +, + +SettingsPanelVM + +, + +Estimation + +, + +InstrumentInfo + +, + +InstrumentVM + +, + +ExcelIO + +, + +CsvExporter + +, and + +Experiment + + classes are placeholders for the corresponding functionality. +- The `if __name__ == "__main__":` block creates and runs the main application window. + +dehoni: Please translate the selected code into python and use approbriate packages. + +GitHub Copilot: Here is the translated code into Python using the `tkinter` package: + +```python + + +import tkinter as tk +from tkinter import ttk, Menu, filedialog, messagebox + +class MainWindow(tk.Tk): + def __init__(self): + super().__init__() + + self.title("SANS Script") + self.geometry("1400x650") + + self.create_widgets() + + def create_widgets(self): + self.grid_columnconfigure(1, weight=1) + self.grid_rowconfigure(4, weight=1) + + # Menu + self.mnu_main = Menu(self) + self.config(menu=self.mnu_main) + + file_menu = Menu(self.mnu_main, tearoff=0) + self.mnu_main.add_cascade(label="_File", menu=file_menu) + file_menu.add_command(label="Export Table to Excel", command=self.mnu_export_to_excel_click) + file_menu.add_command(label="Save as CSV", command=self.mnu_save_csv_click) + file_menu.add_separator() + file_menu.add_command(label="E_xit", command=self.mnu_exit_click) + + instrument_menu = Menu(self.mnu_main, tearoff=0) + self.mnu_main.add_cascade(label="_Instrument", menu=instrument_menu) + self.is_larmor = tk.BooleanVar() + self.is_loq = tk.BooleanVar() + self.is_sans2d = tk.BooleanVar() + self.is_zoom = tk.BooleanVar() + instrument_menu.add_checkbutton(label="LARMOR", variable=self.is_larmor) + instrument_menu.add_checkbutton(label="LOQ", variable=self.is_loq) + instrument_menu.add_checkbutton(label="SANS2D", variable=self.is_sans2d) + instrument_menu.add_checkbutton(label="ZOOM", variable=self.is_zoom) + + # Buttons + button_frame = ttk.Frame(self) + button_frame.grid(row=1, column=0, columnspan=3, pady=10) + self.btn_open_excel = ttk.Button(button_frame, text="Load Excel File", command=self.btn_open_excel_click) + self.btn_open_excel.pack(side=tk.LEFT, padx=5) + self.btn_create_new_table = ttk.Button(button_frame, text="Create New Table", command=self.btn_create_new_table_click) + self.btn_create_new_table.pack(side=tk.LEFT, padx=5) + + # Settings Panel + self.pnl_settings = SettingsPanel(self) + self.pnl_settings.grid(row=2, column=0, columnspan=3, sticky="ew") + + # Estimation Panel + self.pnl_estimate = EstimationPanel(self) + self.pnl_estimate.grid(row=3, column=0, columnspan=3, sticky="ew") + + # Data Grid + self.data_grid = ttk.Treeview(self, columns=("POSITION", "TRANS", "TRANS__WAIT", "SANS", "SANS__WAIT", "PERIOD", "SAMPLE__IDENTIFICATION__STRING", "BACKGROUND", "THICKNESS", "TEMPERATURE", "TEMPERATURE2", "FIELD", "SHEAR__RATE__1", "SHEAR__RATE__2", "SHEAR__ANGLE__1", "SHEAR__ANGLE__2", "PRE__COMMAND", "POST__COMMAND", "RB__NUMBER"), show="headings") + self.data_grid.grid(row=1, column=1, rowspan=4, columnspan=2, sticky="nsew", padx=5, pady=5) + for col in self.data_grid["columns"]: + self.data_grid.heading(col, text=col.replace("__", " ")) + + def btn_open_excel_click(self): + filepath = filedialog.askopenfilename(defaultextension=".xlsx", filetypes=[("Excel Files", "*.xls;*.xlsx")]) + if filepath: + try: + excel = ExcelIO() + excel.open_excel_workbook(filepath) + exps = excel.extract_experiments() + self.data_grid.delete(*self.data_grid.get_children()) + for exp in exps: + self.data_grid.insert("", "end", values=exp) + except Exception as err: + messagebox.showerror("SANS Script - Error", "Could not open Excel spreadsheet") + + def btn_create_new_table_click(self): + self.data_grid.delete(*self.data_grid.get_children()) + + def mnu_export_to_excel_click(self): + excel = ExcelIO() + excel.export_data_to_excel(self.data_grid.get_children()) + + def mnu_save_csv_click(self): + filepath = filedialog.asksaveasfilename(defaultextension=".csv", filetypes=[("CSV File", "*.csv")]) + if filepath: + try: + CsvExporter.export(filepath, self.data_grid.get_children()) + except Exception as err: + messagebox.showerror("SANS Script - Error", "Could not save as CSV") + + def mnu_exit_click(self): + self.destroy() + +class SettingsPanel(ttk.Frame): + def __init__(self, parent): + super().__init__(parent) + self.create_widgets() + + def create_widgets(self): + self.preview_button = ttk.Button(self, text="Preview Script", command=self.preview_click) + self.preview_button.pack(side=tk.LEFT, padx=5, pady=5) + self.write_button = ttk.Button(self, text="Write Script", command=self.write_click) + self.write_button.pack(side=tk.LEFT, padx=5, pady=5) + + def preview_click(self): + pass # Implement preview click functionality + + def write_click(self): + pass # Implement write click functionality + +class EstimationPanel(ttk.Frame): + def __init__(self, parent): + super().__init__(parent) + self.create_widgets() + + def create_widgets(self): + self.calculate_button = ttk.Button(self, text="Calculate", command=self.calculate_click) + self.calculate_button.pack(side=tk.LEFT, padx=5, pady=5) + + def calculate_click(self): + pass # Implement calculate click functionality + +class ExcelIO: + def open_excel_workbook(self, filename): + pass # Dummy implementation + + def extract_experiments(self): + return [] # Dummy implementation + + def export_data_to_excel(self, runs): + pass # Dummy implementation + +class CsvExporter: + @staticmethod + def export(filename, runs): + pass # Dummy implementation + +if __name__ == "__main__": + app = MainWindow() + app.mainloop() +``` + +In this Python version: +- The `MainWindow` class inherits from `tk.Tk` and sets up the main window and its widgets. +- The `SettingsPanel` and `EstimationPanel` classes are custom frames for the settings and estimation panels. +- The `ExcelIO` and `CsvExporter` classes are placeholders for the corresponding functionality. +- The `if __name__ == "__main__":` block creates and runs the main application window. + +dehoni: Please translate the selected code into python and use approbriate packages. + +GitHub Copilot: Here is the translated code into Python using the `tkinter` package: + +```python + + +import tkinter as tk +from tkinter import ttk + +class EstimationPanel(ttk.Frame): + def __init__(self, parent): + super().__init__(parent) + self.on_calculate_clicked = None + self.create_widgets() + + def create_widgets(self): + self.calculate_button = ttk.Button(self, text="Calculate", command=self.btn_calculate_click) + self.calculate_button.pack(padx=5, pady=5) + + def btn_calculate_click(self): + if self.on_calculate_clicked is not None: + self.on_calculate_clicked() + +if __name__ == "__main__": + root = tk.Tk() + panel = EstimationPanel(root) + panel.pack() + root.mainloop() +``` + +In this Python version: +- The + +EstimationPanel + + class inherits from `ttk.Frame`. +- The `on_calculate_clicked` attribute is used to store a callback function. +- The `create_widgets` method sets up the UI elements. +- The `btn_calculate_click` method handles the button click event and calls the callback if it is set. +- The `if __name__ == "__main__":` block creates a simple application to demonstrate the + +EstimationPanel + + usage. + +dehoni: Please translate the selected code into python and use approbriate packages. + +GitHub Copilot: Here is the translated code into Python using the `tkinter` package: + +```python + + +import tkinter as tk +from tkinter import ttk + +class EstimationPanel(ttk.Frame): + def __init__(self, parent, view_model): + super().__init__(parent) + self.view_model = view_model + self.on_calculate_clicked = None + self.create_widgets() + + def create_widgets(self): + self.pack(fill=tk.BOTH, expand=True) + + label = ttk.Label(self, text="Estimated Script Time", font=("Helvetica", 16, "bold")) + label.pack(pady=10) + + grid_frame = ttk.Frame(self) + grid_frame.pack(padx=10, pady=10) + + # Est. count rate + ttk.Label(grid_frame, text="Est. count rate:").grid(row=0, column=0, sticky=tk.E) + count_rate_frame = ttk.Frame(grid_frame) + count_rate_frame.grid(row=0, column=1, columnspan=2) + self.txt_count_rate = ttk.Entry(count_rate_frame, width=10, textvariable=self.view_model.count_rate) + self.txt_count_rate.pack(side=tk.LEFT, padx=5, pady=5) + ttk.Label(count_rate_frame, text="µA/hr").pack(side=tk.LEFT) + + # Est. time between moves + ttk.Label(grid_frame, text="Est. time between moves:").grid(row=1, column=0, sticky=tk.E) + move_time_frame = ttk.Frame(grid_frame) + move_time_frame.grid(row=1, column=1, columnspan=2) + self.txt_move_time = ttk.Entry(move_time_frame, width=10, textvariable=self.view_model.move_time) + self.txt_move_time.pack(side=tk.LEFT, padx=5, pady=5) + ttk.Label(move_time_frame, text="seconds").pack(side=tk.LEFT) + + # Est. script time + ttk.Label(grid_frame, text="Est. script time:").grid(row=2, column=0, sticky=tk.E) + script_time_frame = ttk.Frame(grid_frame) + script_time_frame.grid(row=2, column=1, columnspan=2) + self.txt_estimate = ttk.Entry(script_time_frame, width=15, textvariable=self.view_model.script_time, state='readonly') + self.txt_estimate.pack(side=tk.LEFT, padx=5, pady=5) + + # Calculate button + self.btn_calculate = ttk.Button(grid_frame, text="Calculate", command=self.btn_calculate_click) + self.btn_calculate.grid(row=3, column=1, columnspan=2, pady=10) + + def btn_calculate_click(self): + if self.on_calculate_clicked is not None: + self.on_calculate_clicked() + +class EstimationPanelViewModel: + def __init__(self): + self.count_rate = tk.StringVar() + self.move_time = tk.StringVar() + self.script_time = tk.StringVar() + +if __name__ == "__main__": + root = tk.Tk() + view_model = EstimationPanelViewModel() + panel = EstimationPanel(root, view_model) + panel.pack(fill=tk.BOTH, expand=True) + root.mainloop() +``` + +In this Python version: +- The `EstimationPanel` class inherits from `ttk.Frame`. +- The `EstimationPanelViewModel` class is used to bind data to the UI elements. +- The `create_widgets` method sets up the UI elements similar to the XAML layout. +- The `btn_calculate_click` method handles the button click event and calls the callback if it is set. +- The `if __name__ == "__main__":` block creates a simple application to demonstrate the `EstimationPanel` usage. + +dehoni: Please translate the selected code into python and use approbriate packages. + +GitHub Copilot: Here is the translated code into Python using the `tkinter` package: + +```python + + +import tkinter as tk +from MainWindow import MainWindow + +class App: + def __init__(self): + self.root = tk.Tk() + self.main_window = MainWindow(self.root) + + def run(self): + self.root.mainloop() + +if __name__ == "__main__": + app = App() + app.run() +``` + +In this Python version: +- The + +App + + class initializes the main application window using `tkinter`. +- The `run` method starts the main event loop. +- The `if __name__ == "__main__":` block creates an instance of the + +App + + class and runs the application. + +dehoni: Please translate the selected code into python and use approbriate packages. + +GitHub Copilot: Here is the translated code into Python using the `tkinter` package: + +```python + + +import tkinter as tk +from MainWindow import MainWindow + +class App: + def __init__(self): + self.root = tk.Tk() + self.main_window = MainWindow(self.root) + self.main_window.pack(fill=tk.BOTH, expand=True) + + def run(self): + self.root.mainloop() + +if __name__ == "__main__": + app = App() + app.run() +``` + +In this Python version: +- The `App` class initializes the main application window using `tkinter`. +- The `MainWindow` class is imported and instantiated, and it is packed to fill the main window. +- The `run` method starts the main event loop. +- The `if __name__ == "__main__":` block creates an instance of the `App` class and runs the application. + +dehoni: Please translate the selected code into python and use approbriate packages. + +GitHub Copilot: Here is the translated code into Python using the `configparser` package: + +```python + + +import configparser + +config = configparser.ConfigParser() + +config['DEFAULT'] = { + 'LastInstrument': 'LARMOR', + 'SampleHeight': '11', + 'SampleWidth': '11', + 'Geometry': 'Flat Plate', + 'RunOrder': 'Alternate - TRANS first', + 'NumTrans': '1', + 'NumSans': '1', + 'LoopPerRun': 'False', + 'A2SettingSans': 'Large = 25, 16 mm', + 'A2SettingTrans': 'Large = 25, 16 mm', + 'CollectionMode': 'Events' +} + +with open('app_config.ini', 'w') as configfile: + config.write(configfile) +``` + +In this Python version: +- The `configparser` module is used to create and manage the configuration settings. +- The settings from the XML file are added to the `DEFAULT` section of the configuration. +- The configuration is written to a file named `app_config.ini`. + +dehoni: Please translate the selected code into python and use approbriate packages. + +GitHub Copilot: Here is the translated code into Python using the `tkinter` package and the `tkinter` variable classes for data binding: + +```python + + +import tkinter as tk +from tkinter import StringVar +from tkinter import ttk + +class DataGridVM: + def __init__(self): + self._runs = tk.Variable(value=[]) + self.property_changed = [] + + @property + def runs(self): + return self._runs.get() + + @runs.setter + def runs(self, value): + self._runs.set(value) + self.on_property_changed("runs") + + @staticmethod + def wait_for_choices(): + return WaitForUnits.choices() + + def on_property_changed(self, name): + for callback in self.property_changed: + callback(name) + +class WaitForUnits: + @staticmethod + def choices(): + return ["Choice1", "Choice2", "Choice3"] + +class Experiment: + pass # Dummy implementation + +if __name__ == "__main__": + root = tk.Tk() + vm = DataGridVM() + + def on_property_changed(name): + print(f"Property {name} changed") + + vm.property_changed.append(on_property_changed) + + vm.runs = [Experiment(), Experiment()] + print(vm.runs) + + root.mainloop() +``` + +In this Python version: +- The + +DataGridVM + + class uses `tk.Variable` for data binding. +- The + +runs + + property is implemented with getter and setter methods to handle property changes. +- The `on_property_changed` method notifies subscribers when a property changes. +- The + +WaitForUnits + + class provides static choices for the `wait_for_choices` method. +- The + +Experiment + + class is a placeholder for the actual implementation. +- The `if __name__ == "__main__":` block demonstrates how to use the + +DataGridVM + + class and handle property changes. + +dehoni: Please translate the selected code into python and use approbriate packages. + +GitHub Copilot: Here is the translated code into Python using the `tkinter` package and the `tkinter` variable classes for data binding: + +```python + + +import tkinter as tk + +class EstimationPanelVM: + def __init__(self): + self.property_changed = [] + + def on_property_changed(self, name): + for callback in self.property_changed: + callback(name) + +if __name__ == "__main__": + root = tk.Tk() + vm = EstimationPanelVM() + + def on_property_changed(name): + print(f"Property {name} changed") + + vm.property_changed.append(on_property_changed) + + # Example of triggering property change + vm.on_property_changed("example_property") + + root.mainloop() +``` + +In this Python version: +- The + +EstimationPanelVM + + class uses a list to store property change callbacks. +- The `on_property_changed` method notifies subscribers when a property changes. +- The `if __name__ == "__main__":` block demonstrates how to use the + +EstimationPanelVM + + class and handle property changes. + +dehoni: Please translate the selected code into python and use approbriate packages. + +GitHub Copilot: Here is the translated code into Python using the `tkinter` package and the `tkinter` variable classes for data binding: + +```python + + +import tkinter as tk +from tkinter import StringVar, BooleanVar + +class InstrumentVM: + def __init__(self, settings, estimation): + self.settings = settings + self.estimation = estimation + + self.loq_aperture_sizes = [A2Setting.Large, A2Setting.Medium, A2Setting.Small] + self.sans2d_aperture_sizes = [A2Setting.XLarge, A2Setting.Large, A2Setting.Medium, A2Setting.Small] + self.larmor_aperture_sizes = [A2Setting.Larmor] + self.zoom_aperture_sizes = [A2Setting.Large, A2Setting.Medium, A2Setting.Small] + + self.is_larmor = BooleanVar(value=True) + self.is_loq = BooleanVar(value=False) + self.is_sans2d = BooleanVar(value=False) + self.is_zoom = BooleanVar(value=False) + + self.is_larmor.trace_add("write", self.update_instrument) + self.is_loq.trace_add("write", self.update_instrument) + self.is_sans2d.trace_add("write", self.update_instrument) + self.is_zoom.trace_add("write", self.update_instrument) + + self.update_instrument() + + def update_instrument(self, *args): + if self.is_larmor.get(): + self.set_instrument(True, False, False, False, "A1, S1 Setting:", self.larmor_aperture_sizes, 40) + elif self.is_loq.get(): + self.set_instrument(False, True, False, False, "A2 Setting:", self.loq_aperture_sizes, 170) + elif self.is_sans2d.get(): + self.set_instrument(False, False, True, False, "A6 Setting:", self.sans2d_aperture_sizes, 40) + elif self.is_zoom.get(): + self.set_instrument(False, False, False, True, "A6 Setting:", self.zoom_aperture_sizes, 40) + + def set_instrument(self, larmor, loq, sans2d, zoom, a2_label, a2_choices, count_rate): + self.is_larmor.set(larmor) + self.is_loq.set(loq) + self.is_sans2d.set(sans2d) + self.is_zoom.set(zoom) + + InstrumentInfo.script_generator = Scripting.GeniePythonScriptGenerator(larmor, loq, zoom, sans2d) + InstrumentInfo.a2_label = a2_label + InstrumentInfo.a2_choices = a2_choices + InstrumentInfo.a2_trans_enabled = True + InstrumentInfo.collection_mode_enabled = True + self.estimation.count_rate = count_rate + self.settings.a2_setting_sans = Properties.Settings.Default.a2_setting_sans + self.settings.a2_setting_trans = Properties.Settings.Default.a2_setting_trans + +class A2Setting: + Large = "Large" + Medium = "Medium" + Small = "Small" + XLarge = "XLarge" + Larmor = "Larmor" + +class InstrumentInfo: + script_generator = None + a2_label = "" + a2_choices = [] + a2_trans_enabled = False + collection_mode_enabled = False + +class Scripting: + class GeniePythonScriptGenerator: + def __init__(self, larmor, loq, zoom, sans2d): + pass # Dummy implementation + +class Properties: + class Settings: + class Default: + a2_setting_sans = "Large" + a2_setting_trans = "Large" + +class ExperimentSettings: + def __init__(self): + self.a2_setting_sans = "" + self.a2_setting_trans = "" + +class Estimation: + def __init__(self): + self.count_rate = 0 + +if __name__ == "__main__": + root = tk.Tk() + settings = ExperimentSettings() + estimation = Estimation() + vm = InstrumentVM(settings, estimation) + + def on_property_changed(name): + print(f"Property {name} changed") + + vm.is_larmor.trace_add("write", lambda *args: on_property_changed("is_larmor")) + vm.is_loq.trace_add("write", lambda *args: on_property_changed("is_loq")) + vm.is_sans2d.trace_add("write", lambda *args: on_property_changed("is_sans2d")) + vm.is_zoom.trace_add("write", lambda *args: on_property_changed("is_zoom")) + + root.mainloop() +``` + +In this Python version: +- The + +InstrumentVM + + class uses `BooleanVar` for data binding. +- The `update_instrument` method updates the instrument settings based on the selected instrument. +- The `set_instrument` method sets the instrument-specific properties. +- The + +A2Setting + +, + +InstrumentInfo + +, + +Scripting + +, + +Properties + +, + +ExperimentSettings + +, and + +Estimation + + classes are placeholders for the actual implementation. +- The `if __name__ == "__main__":` block demonstrates how to use the + +InstrumentVM + + class and handle property changes. + +dehoni: Please translate the selected code into python and use approbriate packages. + +GitHub Copilot: Here is the translated code into Python using the `tkinter` package and the `tkinter` variable classes for data binding: + +```python + + +import tkinter as tk +from tkinter import StringVar + +class SettingsPanelVM: + def __init__(self, inst_info=None): + self.property_changed = [] + + self._experiment = ExperimentSettings() + self._inst_info = inst_info if inst_info else InstrumentInfo() + + def on_property_changed(self, name): + for callback in self.property_changed: + callback(name) + + @property + def experiment(self): + return self._experiment + + @experiment.setter + def experiment(self, value): + self._experiment = value + self.on_property_changed("experiment") + + @property + def inst_info(self): + return self._inst_info + + @inst_info.setter + def inst_info(self, value): + self._inst_info = value + self.on_property_changed("inst_info") + + @staticmethod + def order_choices(): + return RunOrder.choices() + + @staticmethod + def geometry_choices(): + return Geometry.choices() + + @staticmethod + def collection_mode_choices(): + return DataCollection.choices() + +class RunOrder: + @staticmethod + def choices(): + return ["Choice1", "Choice2", "Choice3"] + +class Geometry: + @staticmethod + def choices(): + return ["Geometry1", "Geometry2", "Geometry3"] + +class DataCollection: + @staticmethod + def choices(): + return ["Mode1", "Mode2", "Mode3"] + +class ExperimentSettings: + pass # Dummy implementation + +class InstrumentInfo: + pass # Dummy implementation + +if __name__ == "__main__": + root = tk.Tk() + vm = SettingsPanelVM() + + def on_property_changed(name): + print(f"Property {name} changed") + + vm.property_changed.append(on_property_changed) + + vm.experiment = ExperimentSettings() + vm.inst_info = InstrumentInfo() + + root.mainloop() +``` + +In this Python version: +- The + +SettingsPanelVM + + class uses a list to store property change callbacks. +- The `on_property_changed` method notifies subscribers when a property changes. +- The + +experiment + + and `inst_info` properties are implemented with getter and setter methods to handle property changes. +- The `order_choices`, `geometry_choices`, and `collection_mode_choices` methods provide static choices for the combo boxes. +- The + +RunOrder + +, + +Geometry + +, + +DataCollection + +, + +ExperimentSettings + +, and + +InstrumentInfo + + classes are placeholders for the actual implementation. +- The `if __name__ == "__main__":` block demonstrates how to use the + +SettingsPanelVM + + class and handle property changes. + +dehoni: Please translate the selected code into python and use approbriate packages. + +GitHub Copilot: Here is the translated code into Python: + +```python + + +from datetime import datetime, timedelta + +class GeniePythonScriptGenerator: + def __init__(self, is_larmor, is_loq, is_zoom, is_sans2d): + self.indent = " " + self.is_larmor = is_larmor + self.is_loq = is_loq + self.is_zoom = is_zoom + self.is_sans2d = is_sans2d + + def generate_script(self, runs, settings, scriptname): + script = f"# Script created by SANSScript at {datetime.now()}\n\n" + script += self.generate_header(scriptname) + script += self.generate_setup(settings) + script += self.generate_runs(runs, settings) + script += "\n" + return script + + def get_indent_string(self, n): + return self.indent * n + + def generate_header(self, scriptname): + script = "import sys\n" + script += "from genie_python import genie as g, BLOCK_NAMES as b\n" + script += "sys.path.append(r\"c:\\instrument\\scripts\")\n" + + if self.is_larmor: + script += "from instrument.larmor import * # pylint: disable=wildcard-import, unused-wildcard-import\n" + if self.is_loq: + script += "from instrument.loq import * # pylint: disable=wildcard-import, unused-wildcard-import\n" + if self.is_zoom: + script += "from instrument.zoom import * # pylint: disable=wildcard-import, unused-wildcard-import\n" + if self.is_sans2d: + script += "from instrument.sans2d import * # pylint: disable=wildcard-import, unused-wildcard-import\n" + + script += f"\ndef {scriptname}():\n" + return script + + def generate_setup(self, settings): + script = "" + script += self.get_indent_string(1) + f"g.change_sample_par('Width', '{settings.sample_width}')\n" + script += self.get_indent_string(1) + f"g.change_sample_par('height', '{settings.sample_height}')\n" + script += self.get_indent_string(1) + f"g.change_sample_par('Geometry', '{settings.sample_geometry}')\n\n" + return script + + def check_positions(self, runs): + script = "" + if self.is_larmor: + position_list = ",".join([exp.position for exp in runs if exp.position]) + if position_list: + script += self.get_indent_string(1) + f"if check_move_pos(['{position_list}']) != True:\n" + script += self.get_indent_string(2) + "print('Invalid positions - script aborted')\n" + script += self.get_indent_string(2) + "return\n\n" + return script + + def generate_runs(self, runs, settings): + script = self.check_positions(runs) + if not runs: + return script + + if settings.order == "AllTrans": + script += self.get_indent_string(1) + f"for i in range({settings.num_trans}):\n" + for exp in runs: + if exp.trans: + script = self.do_trans_run(script, exp, 2, settings) + script += self.get_indent_string(1) + f"for i in range({settings.num_sans}):\n" + for exp in runs: + if exp.sans: + script = self.do_sans_run(script, exp, 2, settings) + elif settings.order == "AllSans": + script += self.get_indent_string(1) + f"for i in range({settings.num_sans}):\n" + for exp in runs: + if exp.sans: + script = self.do_sans_run(script, exp, 2, settings) + if settings.num_trans and any(exp.trans for exp in runs): + script += self.get_indent_string(1) + f"for i in range({settings.num_trans}):\n" + for exp in runs: + if exp.trans: + script = self.do_trans_run(script, exp, 2, settings) + else: + script += self.get_indent_string(1) + f"num_sans = {settings.num_sans}\n" + script += self.get_indent_string(1) + f"num_trans = {settings.num_trans}\n" + script += self.get_indent_string(1) + "count = 0\n" + script += self.get_indent_string(1) + "while True:\n" + indent = 2 + for exp in runs: + if settings.order == "TransFirst": + if exp.trans: + script += self.get_indent_string(indent) + "if count < num_trans:\n" + script = self.do_trans_run(script, exp, indent + 1, settings) + if exp.sans: + script += self.get_indent_string(indent) + "if count < num_sans:\n" + script = self.do_sans_run(script, exp, indent + 1, settings) + else: + if exp.sans: + script += self.get_indent_string(indent) + "if count < num_sans:\n" + script = self.do_sans_run(script, exp, indent + 1, settings) + if exp.trans: + script += self.get_indent_string(indent) + "if count < num_trans:\n" + script = self.do_trans_run(script, exp, indent + 1, settings) + script += self.get_indent_string(indent) + "count += 1\n" + script += self.get_indent_string(indent) + "if count >= num_trans and count >= num_sans: break\n" + return script + + def do_trans_run(self, script, exp, indent, settings): + script += self.do_sample_environment(exp, indent) + waitfor = "uamps" + rtype = "histogram" + fulltitle = f"{{{exp.sample}}}" + if settings.collection_mode == "Events": + rtype = "event" + if exp.trans_wait == "Frames": + waitfor = "frames" + elif exp.trans_wait == "Seconds": + waitfor = "seconds" + elif exp.trans_wait == "Minutes": + waitfor = "minutes" + if self.is_larmor or self.is_zoom: + script += self.get_indent_string(indent) + f"do_trans(title='{fulltitle}', pos='{exp.position}', {waitfor}={exp.trans}, thickness={exp.thickness})\n" + elif self.is_loq: + script += self.get_indent_string(indent) + f"do_trans(title='{fulltitle}', pos='{exp.position}', {waitfor}={exp.trans}, thickness={exp.thickness}, aperture='small')\n" + else: + script += self.get_indent_string(indent) + f"do_trans(title='{fulltitle}', pos='{exp.position}', {waitfor}={exp.trans}, thickness={exp.thickness}, dae='transmission', aperture='{self.aperture_string(settings.a2_setting_trans)}')\n" + script += self.get_indent_string(indent) + exp.post_command + "\n" + return script + + def do_sans_run(self, script, exp, indent, settings): + script += self.do_sample_environment(exp, indent) + waitfor = "uamps" + rtype = "histogram" + fulltitle = f"{{{exp.sample}}}" + if exp.background: + fulltitle += f"_{{{exp.background}}}" + if settings.collection_mode in ["Events", "TRANSSANS_Events"]: + rtype = "event" + elif settings.collection_mode == "SESANS": + rtype = "sesans" + if settings.collection_mode in ["TRANSSANS_Events", "TRANSSANS_Histogram"]: + fulltitle += "_TRANS" + if exp.sans_wait == "Frames": + waitfor = "frames" + elif exp.sans_wait == "Seconds": + waitfor = "seconds" + elif exp.sans_wait == "Minutes": + waitfor = "minutes" + if self.is_larmor or self.is_zoom: + script += self.get_indent_string(indent) + f"do_sans(title='{fulltitle}', pos='{exp.position}', {waitfor}={exp.sans}, thickness={exp.thickness}, dae='{rtype}')\n" + else: + script += self.get_indent_string(indent) + f"do_sans(title='{fulltitle}', pos='{exp.position}', {waitfor}={exp.sans}, thickness={exp.thickness}, dae='{rtype}', aperture='{self.aperture_string(settings.a2_setting_sans)}')\n" + script += self.get_indent_string(indent) + exp.post_command + "\n" + return script + + def aperture_string(self, size): + if size in [A2Setting.Large, A2Setting.Larmor]: + return "large" + elif size in [A2Setting.Medium, A2Setting.Larmor]: + return "medium" + elif size in [A2Setting.Small, A2Setting.Larmor]: + return "small" + elif size in [A2Setting.XLarge, A2Setting.Larmor]: + return "xlarge" + return "" + + def do_sample_environment(self, exp, indent): + script = "" + if exp.temperature1: + temp1 = float(exp.temperature1) + temp1low = temp1 - 1.0 + temp1high = temp1 + 1.0 + if self.is_loq: + script += self.get_indent_string(indent) + f"g.cset(internal_setpoint_1={exp.temperature1})\n" + script += self.get_indent_string(indent) + f"g.cset(\"Julabo_1_ext\", lowlimit={temp1low}, highlimit={temp1high}, runcontrol=True)\n" + else: + script += self.get_indent_string(indent) + f"g.cset(JULABO1_SP={exp.temperature1})\n" + script += self.get_indent_string(indent) + f"g.cset(\"JULABO1_TEMP\", lowlimit={temp1low}, highlimit={temp1high}, runcontrol=True)\n" + if exp.temperature2: + temp2 = float(exp.temperature2) + temp2low = temp2 - 1.0 + temp2high = temp2 + 1.0 + if self.is_loq: + script += self.get_indent_string(indent) + f"g.cset(internal_setpoint_2={exp.temperature2})\n" + script += self.get_indent_string(indent) + f"g.cset(\"Julabo_2_ext\", lowlimit={temp2low}, highlimit={temp2high}, runcontrol=True)\n" + else: + script += self.get_indent_string(indent) + f"g.cset(JULABO2_SP={exp.temperature2})\n" + script += self.get_indent_string(indent) + f"g.cset(\"JULABO2_TEMP\", lowlimit={temp2low}, highlimit={temp2high}, runcontrol=True)\n" + if exp.pre_command: + script += self.get_indent_string(indent) + exp.pre_command + "\n" + if exp.field: + script += self.get_indent_string(indent) + f"g.cset(FIELD={exp.field})\n" + if exp.shear_rate1: + script += self.get_indent_string(indent) + f"g.cset(SHEAR_RATE_1={exp.shear_rate1})\n" + if exp.shear_rate2: + script += self.get_indent_string(indent) + f"g.cset(SHEAR_RATE_2={exp.shear_rate2})\n" + if exp.shear_angle1: + script += self.get_indent_string(indent) + f"g.cset(SHEAR_ANGLE_1={exp.shear_angle1})\n" + if exp.shear_angle2: + script += self.get_indent_string(indent) + f"g.cset(SHEAR_ANGLE_2={exp.shear_angle2})\n" + return script + + def estimate_run_time(self, runs, settings, estimation): + minutes = 0 + if settings.num_sans > 0: + minutes += self.calculate_time(runs, True, estimation.count_rate, estimation.move_time) * settings.num_sans + if settings.num_trans > 0: + minutes += self.calculate_time(runs, False, estimation.count_rate, estimation.move_time) * settings.num_trans + time = datetime(1, 1, 1, 0, 0, 0) + timedelta(minutes=minutes) + return time.strftime("%H:%M:%S") + + @staticmethod + def calculate_time(runs, is_sans, count_rate, seconds_between): + minutes = 0 + for exp in runs: + if exp.position: + if is_sans: + minutes += GeniePythonScriptGenerator.get_minutes(exp.sans_wait, exp.sans, count_rate) + else: + minutes += GeniePythonScriptGenerator.get_minutes(exp.trans_wait, exp.trans, count_rate) + minutes += seconds_between / 60 + return minutes + + @staticmethod + def get_minutes(wait, length, count_rate): + if wait == "Minutes": + return length + elif wait == "Seconds": + return length / 60.0 + elif wait == "Frames": + return length / (25.0 * 60.0) + else: + return length / (count_rate / 60.0) + +class A2Setting: + Large = "Large" + Medium = "Medium" + Small = "Small" + XLarge = "XLarge" + Larmor = "Larmor" + +class Experiment: + def __init__(self, position="", trans=0, sans=0, trans_wait="", sans_wait="", sample="", background="", thickness=0, pre_command="", post_command="", temperature1="", temperature2="", field="", shear_rate1="", shear_rate2="", shear_angle1="", shear_angle2=""): + self.position = position + self.trans = trans + self.sans = sans + self.trans_wait = trans_wait + self.sans_wait = sans_wait + self.sample = sample + self.background = background + self.thickness = thickness + self.pre_command = pre_command + self.post_command = post_command + self.temperature1 = temperature1 + self.temperature2 = temperature2 + self.field = field + self.shear_rate1 = shear_rate1 + self.shear_rate2 = shear_rate2 + self.shear_angle1 = shear_angle1 + self.shear_angle2 = shear_angle2 + +class ExperimentSettings: + def __init__(self, sample_width="", sample_height="", sample_geometry="", order="", num_trans=0, num_sans=0, collection_mode="", a2_setting_trans="", a2_setting_sans=""): + self.sample_width = sample_width + self.sample_height = sample_height + self.sample_geometry = sample_geometry + self.order = order + self.num_trans = num_trans + self.num_sans = num_sans + self.collection_mode = collection_mode + self.a2_setting_trans = a2_setting_trans + self.a2_setting_sans = a2_setting_sans + +class Estimation: + def __init__(self, count_rate=0, move_time=0): + self.count_rate = count_rate + self.move_time = move_time + +if __name__ == "__main__": + runs = [Experiment(position="pos1", trans=10, sans=20, trans_wait="Minutes", sans_wait="Minutes", sample="Sample1", thickness=1.0)] + settings = ExperimentSettings(sample_width="10", sample_height="10", sample_geometry="Flat", order="AllTrans", num_trans=1, num_sans=1, collection_mode="Events", a2_setting_trans="Large", a2_setting_sans="Large") + estimation = Estimation(count_rate=100, move_time=10) + generator = GeniePythonScriptGenerator(is_larmor=True, is_loq=False, is_zoom=False, is_sans2d=False) + script = generator.generate_script(runs, settings, "my_script") + print(script) + estimated_time = generator.estimate_run_time(runs, settings, estimation) + print(f"Estimated run time: {estimated_time}") +``` + +In this Python version: +- The + +GeniePythonScriptGenerator + + class is implemented with methods to generate the script and estimate run time. +- The + +A2Setting + +, + +Experiment + +, + +ExperimentSettings + +, and + +Estimation + + classes are implemented as placeholders for the actual data structures. +- The `if __name__ == "__main__":` block demonstrates how to use the + +GeniePythonScriptGenerator + + class to generate a script and estimate run time. + +dehoni: Please translate the selected code into python and use approbriate packages. + +GitHub Copilot: Here is the translated code into Python using an abstract base class: + +```python + + +from abc import ABC, abstractmethod + +class IScriptGenerator(ABC): + @abstractmethod + def generate_script(self, runs, settings, scriptname): + pass + + @abstractmethod + def estimate_run_time(self, runs, settings, estimation): + pass + +class Experiment: + pass # Dummy implementation + +class ExperimentSettings: + pass # Dummy implementation + +class Estimation: + pass # Dummy implementation +``` + +In this Python version: +- The + +IScriptGenerator + + class is defined as an abstract base class using the `abc` module. +- The `generate_script` and `estimate_run_time` methods are defined as abstract methods. +- The + +Experiment + +, + +ExperimentSettings + +, and + +Estimation + + classes are placeholders for the actual implementations. + +dehoni: Please translate the selected code into python and use approbriate packages. + +GitHub Copilot: Here is the translated code into Python: + +```python + + +from datetime import datetime, timedelta + +class OpenGenieScriptGenerator: + def __init__(self, is_sans2d): + self.is_sans2d = is_sans2d + + def generate_script(self, runs, settings, scriptname): + script = f"# Script created by LOQ Script at {datetime.now()}\n" + script += "# Script MUST be loaded in to Open GENIE using the loadscript command.\n\n" + + if self.is_sans2d: + script += "QUALIFIERS /test\n" + + script += f"LOCAL transCount = 0 sansCount = 0 numTRANS={settings.num_trans} numSANS={settings.num_sans}\n\n" + script += "SETSCRIPTNAME THIS_PROCEDURE()\n\n" + script += f'Sample_par width={settings.sample_width} height={settings.sample_height} geometry="{settings.sample_geometry}"\n\n' + + if self.is_sans2d: + position_list = ",".join([exp.position for exp in runs if exp.position]) + if position_list: + script += f'IF NOT CHECK_MOVE_POS("{position_list}")\n' + script += ' PRINTEN "Invalid positions - script aborted"\n' + script += ' RETURN\n' + script += 'ENDIF\n\n' + script += "IF test\n" + script += " RETURN\n" + script += "ENDIF\n\n" + + if settings.order == "AllTrans": + if settings.loop_per_run: + script += self.do_all_trans_loop_each(runs, settings, True, True) + script += self.do_all_sans_loop_each(runs, settings, True, True) + else: + script += "LOOP\n\n" + script += self.do_all_trans(runs, settings, True, True) + script += self.do_all_sans(runs, settings, True, True) + script += "EXITIF ((transCount >= numTRANS) AND (sansCount >= numSANS))\n\n" + script += "ENDLOOP\n\n" + elif settings.order == "TransFirst": + if settings.loop_per_run: + for exp in runs: + script += self.do_single_trans_loop_each(exp, settings, True, False) + script += self.do_single_sans_loop_each(exp, settings, False, True) + else: + script += "LOOP\n\n" + for exp in runs: + script += self.do_single_trans(exp, settings, True, False) + script += self.do_single_sans(exp, settings, False, True) + script += "transCount = transCount + 1\n" + script += "sansCount = sansCount + 1\n\n" + script += "EXITIF ((transCount >= numTRANS) AND (sansCount >= numSANS))\n\n" + script += "ENDLOOP\n\n" + elif settings.order == "AllSans": + if settings.loop_per_run: + script += self.do_all_sans_loop_each(runs, settings, True, True) + script += self.do_all_trans_loop_each(runs, settings, True, True) + else: + script += "LOOP\n\n" + script += self.do_all_sans(runs, settings, True, True) + script += self.do_all_trans(runs, settings, True, True) + script += "EXITIF ((transCount >= numTRANS) AND (sansCount >= numSANS))\n\n" + script += "ENDLOOP\n\n" + elif settings.order == "SansFirst": + if settings.loop_per_run: + for exp in runs: + script += self.do_single_sans_loop_each(exp, settings, True, False) + script += self.do_single_trans_loop_each(exp, settings, False, True) + else: + script += "LOOP\n\n" + for exp in runs: + script += self.do_single_sans(exp, settings, True, False) + script += self.do_single_trans(exp, settings, False, True) + script += "transCount = transCount + 1\n" + script += "sansCount = sansCount + 1\n\n" + script += "EXITIF ((transCount >= numTRANS) AND (sansCount >= numSANS))\n\n" + script += "ENDLOOP\n\n" + + script += 'PRINTN "SCRIPT COMPLETED "\n\n' + return script + + def do_single_sans(self, exp, settings, do_pre_com, do_post_com): + script = "" + if settings.num_sans > 0 and exp.sans > 0: + script += "IF (sansCount < numSANS)\n" + script += self.set_sans_aperture(settings.a2_setting_sans) + script += self.do_single_run(False, exp, False, do_pre_com, do_post_com, settings) + script += "\nENDIF\n\n" + return script + + def do_single_trans(self, exp, settings, do_pre_com, do_post_com): + script = "" + if settings.num_trans > 0 and exp.trans > 0: + script += "IF (transCount < numTRANS)\n" + script += self.set_trans_aperture(settings.a2_setting_trans) + script += self.do_single_run(True, exp, False, do_pre_com, do_post_com, settings) + script += "\nENDIF\n\n" + return script + + def do_single_sans_loop_each(self, exp, settings, do_pre_com, do_post_com): + script = "" + if settings.num_sans > 0 and exp.sans > 0: + script += self.set_sans_aperture(settings.a2_setting_sans) + script += self.do_single_run(False, exp, True, do_pre_com, do_post_com, settings) + "\n" + return script + + def do_single_trans_loop_each(self, exp, settings, do_pre_com, do_post_com): + script = "" + if settings.num_trans > 0 and exp.trans > 0: + script += self.set_trans_aperture(settings.a2_setting_trans) + script += self.do_single_run(True, exp, True, do_pre_com, do_post_com, settings) + "\n" + return script + + def do_all_sans(self, runs, settings, do_pre_com, do_post_com): + script = "" + if settings.num_sans > 0: + script += self.set_sans_aperture(settings.a2_setting_sans) + script += "IF sansCount < numSANS\n\n" + for exp in runs: + script += self.do_single_run(False, exp, False, do_pre_com, do_post_com, settings) + "\n" + script += "sansCount = sansCount + 1\n\n" + script += "ENDIF\n\n" + return script + + def do_all_trans(self, runs, settings, do_pre_com, do_post_com): + script = "" + if settings.num_trans > 0: + script += self.set_trans_aperture(settings.a2_setting_trans) + script += "IF transCount < numTRANS\n\n" + for exp in runs: + script += self.do_single_run(True, exp, False, do_pre_com, do_post_com, settings) + "\n" + script += "transCount = transCount + 1\n\n" + script += "ENDIF\n\n" + return script + + def do_all_sans_loop_each(self, runs, settings, do_pre_com, do_post_com): + script = "" + if settings.num_sans > 0: + script += self.set_sans_aperture(settings.a2_setting_sans) + for exp in runs: + script += self.do_single_run(False, exp, True, do_pre_com, do_post_com, settings) + "\n" + return script + + def do_all_trans_loop_each(self, runs, settings, do_pre_com, do_post_com): + script = "" + if settings.num_trans > 0: + script += self.set_trans_aperture(settings.a2_setting_trans) + for exp in runs: + script += self.do_single_run(True, exp, True, do_pre_com, do_post_com, settings) + "\n" + return script + + def set_sans_aperture(self, a2_size_sans): + script = "" + if a2_size_sans == "Large": + script += "DO_SANS /LARGE\n\n" + elif a2_size_sans == "Medium": + script += "DO_SANS /MEDIUM\n\n" + elif a2_size_sans == "Small": + script += "DO_SANS /SMALL\n\n" + elif a2_size_sans == "Trans": + script += "DO_SANS /TRANS\n\n" + return script + + def set_trans_aperture(self, a2_size_trans): + script = "" + if not a2_size_trans: + script += "DO_TRANS\n\n" + else: + if a2_size_trans == "Large": + script += "DO_TRANS /LARGE\n\n" + elif a2_size_trans == "Medium": + script += "DO_TRANS /MEDIUM\n\n" + elif a2_size_trans == "Small": + script += "DO_TRANS /SMALL\n\n" + elif a2_size_trans == "Trans": + script += "DO_TRANS /TRANS\n\n" + return script + + def do_single_run(self, is_trans, exp, is_loop, do_pre_com, do_post_com, settings): + script = "" + if exp.trans > 0 or exp.sans > 0: + if exp.rb_number: + script += f"change rbno={exp.rb_number}\n" + if exp.position: + if do_pre_com: + script += self.do_sample_environment(exp) + if is_loop: + if is_trans: + script += "LOOP transCount FROM 1 TO numTRANS\n" + else: + script += "LOOP sansCount FROM 1 TO numSANS\n" + script += self.create_move_command(is_trans, exp, settings) + script += "ENDLOOP\n" + else: + script += self.create_move_command(is_trans, exp, settings) + if do_post_com and exp.post_command: + script += exp.post_command + "\n" + return script + + def do_sample_environment(self, exp): + script = "" + if exp.temperature1: + script += f"CSET TEMP={exp.temperature1}\n" + if exp.temperature2: + script += f"CSET TEMP2={exp.temperature2}\n" + if exp.pre_command: + script += exp.pre_command + "\n" + if exp.field: + script += f"CSET FIELD={exp.field}\n" + if exp.shear_rate1: + script += f"CSET SHEAR_RATE_1={exp.shear_rate1}\n" + if exp.shear_rate2: + script += f"CSET SHEAR_RATE_2={exp.shear_rate2}\n" + if exp.shear_angle1: + script += f"CSET SHEAR_ANGLE_1={exp.shear_angle1}\n" + if exp.shear_angle2: + script += f"CSET SHEAR_ANGLE_2={exp.shear_angle2}\n" + return script + + def create_move_command(self, is_trans, exp, settings): + script = f'MOVE pos="{exp.position}"' + fulltitle = f"{{{exp.sample}}}" + if exp.background: + fulltitle += f"_{{{exp.background}}}" + if exp.thickness: + script += f" thick={exp.thickness}" + else: + script += " thick=1" + if exp.period: + script += f" period={exp.period}" + if is_trans: + script += self.write_wait_for(exp.trans_wait, exp.trans) + if fulltitle: + script += f' title="{fulltitle}_TRANS"' + else: + script += self.write_wait_for(exp.sans_wait, exp.sans) + if fulltitle: + if settings.collection_mode in ["TRANSSANS_Events", "TRANSSANS_Histogram"]: + script += f' title="{fulltitle}_TRANS_SANS"' + else: + script += f' title="{fulltitle}_SANS"' + return script + "\n" + + def write_wait_for(self, waitfor, length): + script = "" + if waitfor: + if waitfor.lower() == "MicroAmps".lower(): + script += f" uAhr={length}" + elif waitfor.lower() == "Minutes".lower(): + script += f" min={length}" + elif waitfor.lower() == "Seconds".lower(): + script += f" seconds={length}" + elif waitfor.lower() == "Frames".lower(): + script += f" frame={length}" + else: + script += f" uAhr={length}" + else: + script += f" uAhr={length}" + return script + + def estimate_run_time(self, runs, settings, estimation): + minutes = 0 + if settings.num_sans > 0: + minutes += self.calculate_time(runs, True, estimation.count_rate, estimation.move_time) * settings.num_sans + if settings.num_trans > 0: + minutes += self.calculate_time(runs, False, estimation.count_rate, estimation.move_time) * settings.num_trans + time = datetime(1, 1, 1, 0, 0, 0) + timedelta(minutes=minutes) + return time.strftime("%H:%M:%S") + + @staticmethod + def calculate_time(runs, is_sans, count_rate, seconds_between): + minutes = 0 + for exp in runs: + if exp.position: + if is_sans: + minutes += OpenGenieScriptGenerator.get_minutes(exp.sans_wait, exp.sans, count_rate) + else: + minutes += OpenGenieScriptGenerator.get_minutes(exp.trans_wait, exp.trans, count_rate) + minutes += seconds_between / 60 + return minutes + + @staticmethod + def get_minutes(wait, length, count_rate): + if wait == "Minutes": + return length + elif wait == "Seconds": + return length / 60.0 + elif wait == "Frames": + return length / (25.0 * 60.0) + else: + return length / (count_rate / 60.0) + +class Experiment: + def __init__(self, position="", trans=0, sans=0, trans_wait="", sans_wait="", sample="", background="", thickness=0, pre_command="", post_command="", temperature1="", temperature2="", field="", shear_rate1="", shear_rate2="", shear_angle1="", shear_angle2="", rb_number="", period=""): + self.position = position + self.trans = trans + self.sans = sans + self.trans_wait = trans_wait + self.sans_wait = sans_wait + self.sample = sample + self.background = background + self.thickness = thickness + self.pre_command = pre_command + self.post_command = post_command + self.temperature1 = temperature1 + self.temperature2 = temperature2 + self.field = field + self.shear_rate1 = shear_rate1 + self.shear_rate2 = shear_rate2 + self.shear_angle1 = shear_angle1 + self.shear_angle2 = shear_angle2 + self.rb_number = rb_number + self.period = period + +class ExperimentSettings: + def __init__(self, sample_width="", sample_height="", sample_geometry="", order="", num_trans=0, num_sans=0, collection_mode="", a2_setting_trans="", a2_setting_sans="", loop_per_run=False): + self.sample_width = sample_width + self.sample_height = sample_height + self.sample_geometry = sample_geometry + self.order = order + self.num_trans = num_trans + self.num_sans = num_sans + self.collection_mode = collection_mode + self.a2_setting_trans = a2_setting_trans + self.a2_setting_sans = a2_setting_sans + self.loop_per_run = loop_per_run + +class Estimation: + def __init__(self, count_rate=0, move_time=0): + self.count_rate = count_rate + self.move_time = move_time + +if __name__ == "__main__": + runs = [Experiment(position="pos1", trans=10, sans=20, trans_wait="Minutes", sans_wait="Minutes", sample="Sample1", thickness=1.0)] + settings = ExperimentSettings(sample_width="10", sample_height="10", sample_geometry="Flat", order="AllTrans", num_trans=1, num_sans=1, collection_mode="Events", a2_setting_trans="Large", a2_setting_sans="Large", loop_per_run=False) + estimation = Estimation(count_rate=100, move_time=10) + generator = OpenGenieScriptGenerator(is_sans2d=True) + script = generator.generate_script(runs, settings, "my_script") + print(script) + estimated_time = generator.estimate_run_time(runs, settings, estimation) + print(f"Estimated run time: {estimated_time}") +``` + +In this Python version: +- The `OpenGenieScriptGenerator` class is implemented with methods to generate the script and estimate run time. +- The `Experiment`, `ExperimentSettings`, and `Estimation` classes are implemented as placeholders for the actual data structures. +- The `if __name__ == "__main__":` block demonstrates how to use the `OpenGenieScriptGenerator` class to generate a script and estimate run time. + +dehoni: Please translate the selected code into python and use approbriate packages. + +GitHub Copilot: Here is the translated code into Python using the `setuptools` package for setting up package metadata: + +```python + + +from setuptools import setup, find_packages + +setup( + name="LOQ_Script_Gui", + version="1.0.0", + description="", + author="", + author_email="", + url="", + packages=find_packages(), + include_package_data=True, + install_requires=[ + # Add your dependencies here + ], + classifiers=[ + "Programming Language :: Python :: 3", + "License :: OSI Approved :: MIT License", + "Operating System :: OS Independent", + ], + python_requires='>=3.6', +) + +# Additional metadata for localization and resource management can be added as needed. +``` + +In this Python version: +- The `setup.py` file is used to define the package metadata and dependencies. +- The `setuptools` package is used to handle the package setup. +- The `find_packages` function is used to automatically discover all packages and subpackages. +- The `install_requires` list can be populated with the required dependencies. +- The `classifiers` list is used to provide additional metadata about the package. + +dehoni: Please translate the selected code into python and use approbriate packages. + +GitHub Copilot: Here is the translated code into Python using the `gettext` package for handling localization and resource management: + +```python + + +import gettext +import os + +class Resources: + def __init__(self): + self.resource_man = None + self.resource_culture = None + + @property + def resource_manager(self): + if self.resource_man is None: + locale_dir = os.path.join(os.path.dirname(__file__), 'locale') + self.resource_man = gettext.translation('resources', localedir=locale_dir, languages=[self.resource_culture]) + return self.resource_man + + @property + def culture(self): + return self.resource_culture + + @culture.setter + def culture(self, value): + self.resource_culture = value + self.resource_man = None # Reset the resource manager to reload with the new culture + +# Example usage +resources = Resources() +resources.culture = 'en_US' # Set the desired culture +_ = resources.resource_manager.gettext + +# Example of using the localized string +print(_("Hello, World!")) +``` + +In this Python version: +- The `gettext` package is used for localization and resource management. +- The + +Resources + + class manages the resource manager and culture settings. +- The `resource_manager` property initializes the `gettext.translation` object based on the specified culture. +- The `culture` property allows setting the desired culture and resets the resource manager to reload with the new culture. +- An example usage is provided to demonstrate how to set the culture and use the localized strings. + +dehoni: Please translate the selected code into python and use approbriate packages. + +GitHub Copilot: Here is the translated code into Python using the `configparser` package for handling resource files: + +```python + + +[resheader] +resmimetype = text/microsoft-resx +version = 2.0 +reader = System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 +writer = System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + +[data] +Name1 = this is my long string +Color1 = Blue +Bitmap1 = [base64 mime encoded serialized .NET Framework object] +Icon1 = [base64 mime encoded string representing a byte array form of the .NET Framework object] + +[metadata] +Name1.comment = this is a comment +Icon1.comment = This is a comment +``` + +In this Python version: +- The `.resx` file is translated into an `.ini` file format using the `configparser` package. +- The `resheader` section contains metadata about the resource file. +- The `data` section contains the actual resource data. +- The `metadata` section contains comments associated with the data. + +To read this `.ini` file in Python, you can use the following code: + +```python + + +import configparser + +config = configparser.ConfigParser() +config.read('resources.ini') + +# Accessing resheader values +resmimetype = config['resheader']['resmimetype'] +version = config['resheader']['version'] +reader = config['resheader']['reader'] +writer = config['resheader']['writer'] + +# Accessing data values +name1 = config['data']['Name1'] +color1 = config['data']['Color1'] +bitmap1 = config['data']['Bitmap1'] +icon1 = config['data']['Icon1'] + +# Accessing metadata comments +name1_comment = config['metadata']['Name1.comment'] +icon1_comment = config['metadata']['Icon1.comment'] + +print(f"Name1: {name1} (Comment: {name1_comment})") +print(f"Color1: {color1}") +print(f"Bitmap1: {bitmap1}") +print(f"Icon1: {icon1} (Comment: {icon1_comment})") +``` + +This code reads the `resources.ini` file and prints the values and comments for the resources. + +dehoni: Please translate the selected code into python and use approbriate packages. + +GitHub Copilot: Here is the translated code into Python using the `configparser` package for handling application settings: + +```python + + +[DEFAULT] +LastInstrument = LARMOR +SampleHeight = 11 +SampleWidth = 11 +Geometry = Flat Plate +RunOrder = Alternate - TRANS first +NumTrans = 1 +NumSans = 1 +LoopPerRun = False +A2SettingSans = Large = 25, 16 mm +A2SettingTrans = Large = 25, 16 mm +CollectionMode = Events +``` + +To read and write these settings in Python, you can use the following code: + +```python + + +import configparser + +class Settings: + def __init__(self, filename='settings.ini'): + self.config = configparser.ConfigParser() + self.filename = filename + self.config.read(self.filename) + + @property + def last_instrument(self): + return self.config['DEFAULT']['LastInstrument'] + + @last_instrument.setter + def last_instrument(self, value): + self.config['DEFAULT']['LastInstrument'] = value + self.save() + + @property + def sample_height(self): + return self.config['DEFAULT']['SampleHeight'] + + @sample_height.setter + def sample_height(self, value): + self.config['DEFAULT']['SampleHeight'] = value + self.save() + + @property + def sample_width(self): + return self.config['DEFAULT']['SampleWidth'] + + @sample_width.setter + def sample_width(self, value): + self.config['DEFAULT']['SampleWidth'] = value + self.save() + + @property + def geometry(self): + return self.config['DEFAULT']['Geometry'] + + @geometry.setter + def geometry(self, value): + self.config['DEFAULT']['Geometry'] = value + self.save() + + @property + def run_order(self): + return self.config['DEFAULT']['RunOrder'] + + @run_order.setter + def run_order(self, value): + self.config['DEFAULT']['RunOrder'] = value + self.save() + + @property + def num_trans(self): + return self.config['DEFAULT']['NumTrans'] + + @num_trans.setter + def num_trans(self, value): + self.config['DEFAULT']['NumTrans'] = value + self.save() + + @property + def num_sans(self): + return self.config['DEFAULT']['NumSans'] + + @num_sans.setter + def num_sans(self, value): + self.config['DEFAULT']['NumSans'] = value + self.save() + + @property + def loop_per_run(self): + return self.config['DEFAULT']['LoopPerRun'] + + @loop_per_run.setter + def loop_per_run(self, value): + self.config['DEFAULT']['LoopPerRun'] = value + self.save() + + @property + def a2_setting_sans(self): + return self.config['DEFAULT']['A2SettingSans'] + + @a2_setting_sans.setter + def a2_setting_sans(self, value): + self.config['DEFAULT']['A2SettingSans'] = value + self.save() + + @property + def a2_setting_trans(self): + return self.config['DEFAULT']['A2SettingTrans'] + + @a2_setting_trans.setter + def a2_setting_trans(self, value): + self.config['DEFAULT']['A2SettingTrans'] = value + self.save() + + @property + def collection_mode(self): + return self.config['DEFAULT']['CollectionMode'] + + @collection_mode.setter + def collection_mode(self, value): + self.config['DEFAULT']['CollectionMode'] = value + self.save() + + def save(self): + with open(self.filename, 'w') as configfile: + self.config.write(configfile) + +# Example usage +settings = Settings() +print(settings.last_instrument) +settings.last_instrument = 'LOQ' +print(settings.last_instrument) +``` + +In this Python version: +- The `settings.ini` file is used to store the application settings. +- The + +Settings + + class provides properties to get and set the settings values. +- The `save` method writes the updated settings back to the `settings.ini` file. +- An example usage is provided to demonstrate how to read and update the settings. + +dehoni: Please translate the selected code into python and use approbriate packages. + +GitHub Copilot: Here is the translated code into Python using the `configparser` package for handling application settings: + +```python + + +[DEFAULT] +LastInstrument = LARMOR +SampleHeight = 11 +SampleWidth = 11 +Geometry = Flat Plate +RunOrder = Alternate - TRANS first +NumTrans = 1 +NumSans = 1 +LoopPerRun = False +A2SettingSans = Large = 25, 16 mm +A2SettingTrans = Large = 25, 16 mm +CollectionMode = Events +``` + +To read and write these settings in Python, you can use the following code: + +```python + + +import configparser + +class Settings: + def __init__(self, filename='settings.ini'): + self.config = configparser.ConfigParser() + self.filename = filename + self.config.read(self.filename) + + @property + def last_instrument(self): + return self.config['DEFAULT']['LastInstrument'] + + @last_instrument.setter + def last_instrument(self, value): + self.config['DEFAULT']['LastInstrument'] = value + self.save() + + @property + def sample_height(self): + return self.config['DEFAULT']['SampleHeight'] + + @sample_height.setter + def sample_height(self, value): + self.config['DEFAULT']['SampleHeight'] = value + self.save() + + @property + def sample_width(self): + return self.config['DEFAULT']['SampleWidth'] + + @sample_width.setter + def sample_width(self, value): + self.config['DEFAULT']['SampleWidth'] = value + self.save() + + @property + def geometry(self): + return self.config['DEFAULT']['Geometry'] + + @geometry.setter + def geometry(self, value): + self.config['DEFAULT']['Geometry'] = value + self.save() + + @property + def run_order(self): + return self.config['DEFAULT']['RunOrder'] + + @run_order.setter + def run_order(self, value): + self.config['DEFAULT']['RunOrder'] = value + self.save() + + @property + def num_trans(self): + return self.config['DEFAULT']['NumTrans'] + + @num_trans.setter + def num_trans(self, value): + self.config['DEFAULT']['NumTrans'] = value + self.save() + + @property + def num_sans(self): + return self.config['DEFAULT']['NumSans'] + + @num_sans.setter + def num_sans(self, value): + self.config['DEFAULT']['NumSans'] = value + self.save() + + @property + def loop_per_run(self): + return self.config['DEFAULT']['LoopPerRun'] + + @loop_per_run.setter + def loop_per_run(self, value): + self.config['DEFAULT']['LoopPerRun'] = value + self.save() + + @property + def a2_setting_sans(self): + return self.config['DEFAULT']['A2SettingSans'] + + @a2_setting_sans.setter + def a2_setting_sans(self, value): + self.config['DEFAULT']['A2SettingSans'] = value + self.save() + + @property + def a2_setting_trans(self): + return self.config['DEFAULT']['A2SettingTrans'] + + @a2_setting_trans.setter + def a2_setting_trans(self, value): + self.config['DEFAULT']['A2SettingTrans'] = value + self.save() + + @property + def collection_mode(self): + return self.config['DEFAULT']['CollectionMode'] + + @collection_mode.setter + def collection_mode(self, value): + self.config['DEFAULT']['CollectionMode'] = value + self.save() + + def save(self): + with open(self.filename, 'w') as configfile: + self.config.write(configfile) + +# Example usage +settings = Settings() +print(settings.last_instrument) +settings.last_instrument = 'LOQ' +print(settings.last_instrument) +``` + +In this Python version: +- The `settings.ini` file is used to store the application settings. +- The `Settings` class provides properties to get and set the settings values. +- The `save` method writes the updated settings back to the `settings.ini` file. +- An example usage is provided to demonstrate how to read and update the settings. + +dehoni: Please translate the selected code into python and use approbriate packages. + +GitHub Copilot: Here is the translated code into Python: + +```python + + +class A2Setting: + X_LARGE = "XLarge = 20x18 mm" + LARGE = "Large = 17 mm" + MEDIUM = "Medium = 12 mm" + SMALL = "Small = 5/1 mm" + TRANS = "Trans = 1 mm" + LARMOR = "" + # LARMOR_LARGE = "Large = 25, 16 mm" + # LARMOR_MEDIUM = "Medium = 20, 14 mm" + # LARMOR_SMALL = "Small = 15, 10 mm" + +# Example usage +if __name__ == "__main__": + print(A2Setting.X_LARGE) + print(A2Setting.LARGE) + print(A2Setting.MEDIUM) + print(A2Setting.SMALL) + print(A2Setting.TRANS) + print(A2Setting.LARMOR) +``` + +In this Python version: +- The + +A2Setting + + class is defined with class attributes for each setting. +- The commented-out settings from the original C# code are also commented out in the Python version. +- An example usage is provided to demonstrate how to access the settings. + +dehoni: Please translate the selected code into python and use approbriate packages. + +GitHub Copilot: Here is the translated code into Python: + +```python + + +class DataCollection: + HISTOGRAM = "Histogram" + EVENTS = "Events" + SESANS = "SESANS" + TRANSSANS_EVENTS = "TRANSSANS_Events" + TRANSSANS_HISTOGRAM = "TRANSSANS_Histogram" + + choices = [ + HISTOGRAM, + TRANSSANS_HISTOGRAM, + EVENTS, + TRANSSANS_EVENTS, + SESANS + ] + + @staticmethod + def get_choices(): + return DataCollection.choices + +# Example usage +if __name__ == "__main__": + print(DataCollection.get_choices()) +``` + +In this Python version: +- The + +DataCollection + + class is defined with class attributes for each data collection type. +- The + +choices + + list contains all the data collection types. +- The `get_choices` static method returns the list of choices. +- An example usage is provided to demonstrate how to access the choices. + +dehoni: Please translate the selected code into python and use approbriate packages. + +GitHub Copilot: Here is the translated code into Python using the `tkinter` package for data binding and validation: + +```python + + +import tkinter as tk + +class Estimation: + def __init__(self): + self._count_rate = tk.DoubleVar(value=40) + self._move_time = tk.DoubleVar(value=120) + self._script_time = tk.StringVar() + self._is_valid = tk.BooleanVar(value=True) + self.property_changed = [] + + self._count_rate.trace_add("write", self.validate) + self._move_time.trace_add("write", self.validate) + + @property + def count_rate(self): + return self._count_rate.get() + + @count_rate.setter + def count_rate(self, value): + self._count_rate.set(value) + self.on_property_changed("count_rate") + + @property + def move_time(self): + return self._move_time.get() + + @move_time.setter + def move_time(self, value): + self._move_time.set(value) + self.on_property_changed("move_time") + + @property + def script_time(self): + return self._script_time.get() + + @script_time.setter + def script_time(self, value): + self._script_time.set(value) + self.on_property_changed("script_time") + + @property + def is_valid(self): + return self._is_valid.get() + + @is_valid.setter + def is_valid(self, value): + self._is_valid.set(value) + self.on_property_changed("is_valid") + + def on_property_changed(self, name): + for callback in self.property_changed: + callback(name) + + def validate(self, *args): + error_message = "" + if self.count_rate < 0: + error_message = "Count rate cannot be negative!" + elif self.move_time < 0: + error_message = "Move time cannot be negative!" + + if error_message: + self.is_valid = False + else: + self.is_valid = True + + return error_message + +# Example usage +if __name__ == "__main__": + def on_property_changed(name): + print(f"Property {name} changed") + + estimation = Estimation() + estimation.property_changed.append(on_property_changed) + + print(estimation.count_rate) + estimation.count_rate = -10 # This should trigger validation and set is_valid to False + print(estimation.is_valid) + estimation.count_rate = 50 # This should trigger validation and set is_valid to True + print(estimation.is_valid) +``` + +In this Python version: +- The + +Estimation + + class uses `tkinter` variable classes (`DoubleVar`, `StringVar`, `BooleanVar`) for data binding. +- The `validate` method checks the validity of `count_rate` and `move_time` and updates the `is_valid` property accordingly. +- The `on_property_changed` method notifies subscribers when a property changes. +- An example usage is provided to demonstrate how to use the + +Estimation + + class and handle property changes. + +dehoni: Please translate the selected code into python and use approbriate packages. + +GitHub Copilot: Here is the translated code into Python using the `tkinter` package for data binding and property change notifications: + +```python + + +import tkinter as tk + +class Experiment: + def __init__(self): + self._position = tk.StringVar() + self._trans = tk.DoubleVar(value=0) + self._trans_wait = tk.StringVar() + self._sans = tk.DoubleVar(value=0) + self._sans_wait = tk.StringVar() + self._period = tk.StringVar() + self._background = tk.StringVar() + self._sample = tk.StringVar() + self._thickness = tk.StringVar() + self._temperature1 = tk.StringVar() + self._temperature2 = tk.StringVar() + self._field = tk.StringVar() + self._shear_rate1 = tk.StringVar() + self._shear_rate2 = tk.StringVar() + self._shear_angle1 = tk.StringVar() + self._shear_angle2 = tk.StringVar() + self._pre_command = tk.StringVar() + self._post_command = tk.StringVar() + self._rb_number = tk.StringVar() + self.property_changed = [] + + self._position.trace_add("write", self.on_property_changed) + self._trans.trace_add("write", self.on_property_changed) + self._trans_wait.trace_add("write", self.on_property_changed) + self._sans.trace_add("write", self.on_property_changed) + self._sans_wait.trace_add("write", self.on_property_changed) + self._period.trace_add("write", self.on_property_changed) + self._background.trace_add("write", self.on_property_changed) + self._sample.trace_add("write", self.on_property_changed) + self._thickness.trace_add("write", self.on_property_changed) + self._temperature1.trace_add("write", self.on_property_changed) + self._temperature2.trace_add("write", self.on_property_changed) + self._field.trace_add("write", self.on_property_changed) + self._shear_rate1.trace_add("write", self.on_property_changed) + self._shear_rate2.trace_add("write", self.on_property_changed) + self._shear_angle1.trace_add("write", self.on_property_changed) + self._shear_angle2.trace_add("write", self.on_property_changed) + self._pre_command.trace_add("write", self.on_property_changed) + self._post_command.trace_add("write", self.on_property_changed) + self._rb_number.trace_add("write", self.on_property_changed) + + def on_property_changed(self, *args): + for callback in self.property_changed: + callback(args[0]) + + @property + def position(self): + return self._position.get() + + @position.setter + def position(self, value): + self._position.set(value) + + @property + def trans(self): + return self._trans.get() + + @trans.setter + def trans(self, value): + self._trans.set(value) + + @property + def trans_wait(self): + return self._trans_wait.get() + + @trans_wait.setter + def trans_wait(self, value): + self._trans_wait.set(value) + + @property + def sans(self): + return self._sans.get() + + @sans.setter + def sans(self, value): + self._sans.set(value) + + @property + def sans_wait(self): + return self._sans_wait.get() + + @sans_wait.setter + def sans_wait(self, value): + self._sans_wait.set(value) + + @property + def period(self): + return self._period.get() + + @period.setter + def period(self, value): + self._period.set(value) + + @property + def background(self): + return self._background.get() + + @background.setter + def background(self, value): + self._background.set(value) + + @property + def sample(self): + return self._sample.get() + + @sample.setter + def sample(self, value): + self._sample.set(value) + + @property + def thickness(self): + return self._thickness.get() + + @thickness.setter + def thickness(self, value): + self._thickness.set(value) + + @property + def temperature1(self): + return self._temperature1.get() + + @temperature1.setter + def temperature1(self, value): + self._temperature1.set(value) + + @property + def temperature2(self): + return self._temperature2.get() + + @temperature2.setter + def temperature2(self, value): + self._temperature2.set(value) + + @property + def field(self): + return self._field.get() + + @field.setter + def field(self, value): + self._field.set(value) + + @property + def shear_rate1(self): + return self._shear_rate1.get() + + @shear_rate1.setter + def shear_rate1(self, value): + self._shear_rate1.set(value) + + @property + def shear_rate2(self): + return self._shear_rate2.get() + + @shear_rate2.setter + def shear_rate2(self, value): + self._shear_rate2.set(value) + + @property + def shear_angle1(self): + return self._shear_angle1.get() + + @shear_angle1.setter + def shear_angle1(self, value): + self._shear_angle1.set(value) + + @property + def shear_angle2(self): + return self._shear_angle2.get() + + @shear_angle2.setter + def shear_angle2(self, value): + self._shear_angle2.set(value) + + @property + def pre_command(self): + return self._pre_command.get() + + @pre_command.setter + def pre_command(self, value): + self._pre_command.set(value) + + @property + def post_command(self): + return self._post_command.get() + + @post_command.setter + def post_command(self, value): + self._post_command.set(value) + + @property + def rb_number(self): + return self._rb_number.get() + + @rb_number.setter + def rb_number(self, value): + self._rb_number.set(value) + +# Example usage +if __name__ == "__main__": + def on_property_changed(name): + print(f"Property {name} changed") + + experiment = Experiment() + experiment.property_changed.append(on_property_changed) + + experiment.position = "New Position" + experiment.trans = 10.5 + experiment.sans = 20.0 + experiment.temperature1 = "25" + experiment.temperature2 = "30" + experiment.field = "1.5T" + experiment.shear_rate1 = "100" + experiment.shear_rate2 = "200" + experiment.shear_angle1 = "45" + experiment.shear_angle2 = "90" + experiment.pre_command = "Pre Command" + experiment.post_command = "Post Command" + experiment.rb_number = "RB12345" +``` + +In this Python version: +- The + +Experiment + + class uses `tkinter` variable classes (`StringVar`, `DoubleVar`) for data binding. +- The `on_property_changed` method notifies subscribers when a property changes. +- Properties are defined for each attribute to get and set the values. +- An example usage is provided to demonstrate how to use the + +Experiment + + class and handle property changes. + +dehoni: Please translate the selected code into python and use approbriate packages. + +GitHub Copilot: Here is the translated code into Python using the `tkinter` package for data binding and property change notifications: + +```python + + +import tkinter as tk +from configparser import ConfigParser + +class ExperimentSettings: + def __init__(self, config_file='settings.ini'): + self.config = ConfigParser() + self.config.read(config_file) + self.config_file = config_file + + self._order = tk.StringVar(value=self.config['DEFAULT'].get('RunOrder', 'TransFirst')) + self._num_sans = tk.IntVar(value=self.config['DEFAULT'].getint('NumSans', 1)) + self._num_trans = tk.IntVar(value=self.config['DEFAULT'].getint('NumTrans', 1)) + self._loop_per_run = tk.BooleanVar(value=self.config['DEFAULT'].getboolean('LoopPerRun', False)) + self._a2_setting_sans = tk.StringVar(value=self.config['DEFAULT'].get('A2SettingSans', 'Large')) + self._a2_setting_trans = tk.StringVar(value=self.config['DEFAULT'].get('A2SettingTrans', 'Large')) + self._sample_geometry = tk.StringVar(value=self.config['DEFAULT'].get('Geometry', 'Flat Plate')) + self._sample_height = tk.DoubleVar(value=self.config['DEFAULT'].getfloat('SampleHeight', 11)) + self._sample_width = tk.DoubleVar(value=self.config['DEFAULT'].getfloat('SampleWidth', 11)) + self._sample_width_enabled = tk.BooleanVar(value=True) + self._collection_mode = tk.StringVar(value=self.config['DEFAULT'].get('CollectionMode', 'Events')) + self._is_valid = tk.BooleanVar(value=True) + self.property_changed = [] + + self._order.trace_add("write", self.on_property_changed) + self._num_sans.trace_add("write", self.on_property_changed) + self._num_trans.trace_add("write", self.on_property_changed) + self._loop_per_run.trace_add("write", self.on_property_changed) + self._a2_setting_sans.trace_add("write", self.on_property_changed) + self._a2_setting_trans.trace_add("write", self.on_property_changed) + self._sample_geometry.trace_add("write", self.on_property_changed) + self._sample_height.trace_add("write", self.on_property_changed) + self._sample_width.trace_add("write", self.on_property_changed) + self._collection_mode.trace_add("write", self.on_property_changed) + + def on_property_changed(self, *args): + for callback in self.property_changed: + callback(args[0]) + self.save() + + def save(self): + self.config['DEFAULT']['RunOrder'] = self._order.get() + self.config['DEFAULT']['NumSans'] = str(self._num_sans.get()) + self.config['DEFAULT']['NumTrans'] = str(self._num_trans.get()) + self.config['DEFAULT']['LoopPerRun'] = str(self._loop_per_run.get()) + self.config['DEFAULT']['A2SettingSans'] = self._a2_setting_sans.get() + self.config['DEFAULT']['A2SettingTrans'] = self._a2_setting_trans.get() + self.config['DEFAULT']['Geometry'] = self._sample_geometry.get() + self.config['DEFAULT']['SampleHeight'] = str(self._sample_height.get()) + self.config['DEFAULT']['SampleWidth'] = str(self._sample_width.get()) + self.config['DEFAULT']['CollectionMode'] = self._collection_mode.get() + with open(self.config_file, 'w') as configfile: + self.config.write(configfile) + + @property + def order(self): + return self._order.get() + + @order.setter + def order(self, value): + self._order.set(value) + + @property + def num_sans(self): + return self._num_sans.get() + + @num_sans.setter + def num_sans(self, value): + self._num_sans.set(value) + + @property + def num_trans(self): + return self._num_trans.get() + + @num_trans.setter + def num_trans(self, value): + self._num_trans.set(value) + + @property + def loop_per_run(self): + return self._loop_per_run.get() + + @loop_per_run.setter + def loop_per_run(self, value): + self._loop_per_run.set(value) + + @property + def a2_setting_sans(self): + return self._a2_setting_sans.get() + + @a2_setting_sans.setter + def a2_setting_sans(self, value): + self._a2_setting_sans.set(value) + + @property + def a2_setting_trans(self): + return self._a2_setting_trans.get() + + @a2_setting_trans.setter + def a2_setting_trans(self, value): + self._a2_setting_trans.set(value) + + @property + def sample_geometry(self): + return self._sample_geometry.get() + + @sample_geometry.setter + def sample_geometry(self, value): + self._sample_geometry.set(value) + self.sample_width_enabled = value != 'Disc' + + @property + def sample_height(self): + return self._sample_height.get() + + @sample_height.setter + def sample_height(self, value): + self._sample_height.set(value) + if not self.sample_width_enabled: + self.sample_width = value + + @property + def sample_width(self): + return self._sample_width.get() + + @sample_width.setter + def sample_width(self, value): + self._sample_width.set(value) + + @property + def sample_width_enabled(self): + return self._sample_width_enabled.get() + + @sample_width_enabled.setter + def sample_width_enabled(self, value): + self._sample_width_enabled.set(value) + if not value: + self.sample_width = self.sample_height + + @property + def collection_mode(self): + return self._collection_mode.get() + + @collection_mode.setter + def collection_mode(self, value): + self._collection_mode.set(value) + + @property + def is_valid(self): + return self._is_valid.get() + + @is_valid.setter + def is_valid(self, value): + self._is_valid.set(value) + + def validate(self): + error_message = "" + if self.num_sans < 0: + error_message = "Number of SANS runs cannot be negative!" + elif self.num_trans < 0: + error_message = "Number of TRANS runs cannot be negative!" + elif self.sample_height <= 0 or self.sample_height > 20: + error_message = "Sample height must be between 0 and 20" + elif self.sample_width <= 0 or self.sample_width > 20: + error_message = "Sample width must be between 0 and 20" + + if error_message: + self.is_valid = False + else: + self.is_valid = True + + return error_message + +# Example usage +if __name__ == "__main__": + def on_property_changed(name): + print(f"Property {name} changed") + + settings = ExperimentSettings() + settings.property_changed.append(on_property_changed) + + settings.order = "SansFirst" + settings.num_sans = 2 + settings.num_trans = 3 + settings.sample_height = 15 + settings.sample_width = 10 + settings.collection_mode = "Histogram" + print(settings.validate()) +``` + +In this Python version: +- The + +ExperimentSettings + + class uses `tkinter` variable classes (`StringVar`, `IntVar`, `DoubleVar`, `BooleanVar`) for data binding. +- The `on_property_changed` method notifies subscribers when a property changes and saves the settings to the `settings.ini` file. +- Properties are defined for each attribute to get and set the values. +- The `validate` method checks the validity of the settings and updates the `is_valid` property accordingly. +- An example usage is provided to demonstrate how to use the + +ExperimentSettings + + class and handle property changes. + +dehoni: Please translate the selected code into python and use approbriate packages. + +GitHub Copilot: Here is the translated code into Python: + +```python + + +class Geometry: + DISC = "Disc" + CYLINDRICAL = "Cylindrical" + FLAT_PLATE = "Flat Plate" + SINGLE_CRYSTAL = "Single Crystal" + + choices = [ + DISC, + CYLINDRICAL, + FLAT_PLATE, + SINGLE_CRYSTAL + ] + + @staticmethod + def get_choices(): + return Geometry.choices + +# Example usage +if __name__ == "__main__": + print(Geometry.get_choices()) +``` + +In this Python version: +- The + +Geometry + + class is defined with class attributes for each geometry type. +- The + +choices + + list contains all the geometry types. +- The `get_choices` static method returns the list of choices. +- An example usage is provided to demonstrate how to access the choices. + +dehoni: Please translate the selected code into python and use approbriate packages. + +GitHub Copilot: Here is the translated code into Python using the `tkinter` package for data binding and property change notifications: + +```python + + +import tkinter as tk +from tkinter import StringVar, BooleanVar +from typing import List + +class InstrumentInfo: + property_changed = [] + static_property_changed = [] + + def __init__(self): + self._a2_label = StringVar(value="A2 Setting:") + self._a2_choices = tk.Variable(value=["Large", "Medium", "Small"]) + self._a2_trans_enabled = BooleanVar(value=True) + self._collection_mode_enabled = BooleanVar(value=False) + self._script_generator = None + + self._a2_label.trace_add("write", self.on_property_changed) + self._a2_choices.trace_add("write", self.on_property_changed) + self._a2_trans_enabled.trace_add("write", self.on_property_changed) + self._collection_mode_enabled.trace_add("write", self.on_property_changed) + + def on_property_changed(self, *args): + for callback in self.property_changed: + callback(args[0]) + + @staticmethod + def on_static_property_changed(property_name): + for callback in InstrumentInfo.static_property_changed: + callback(property_name) + + @property + def a2_label(self): + return self._a2_label.get() + + @a2_label.setter + def a2_label(self, value): + self._a2_label.set(value) + self.on_static_property_changed("a2_label") + + @property + def a2_choices(self): + return self._a2_choices.get() + + @a2_choices.setter + def a2_choices(self, value: List[str]): + self._a2_choices.set(value) + self.on_static_property_changed("a2_choices") + + @property + def a2_trans_enabled(self): + return self._a2_trans_enabled.get() + + @a2_trans_enabled.setter + def a2_trans_enabled(self, value): + self._a2_trans_enabled.set(value) + self.on_static_property_changed("a2_trans_enabled") + + @property + def collection_mode_enabled(self): + return self._collection_mode_enabled.get() + + @collection_mode_enabled.setter + def collection_mode_enabled(self, value): + self._collection_mode_enabled.set(value) + self.on_static_property_changed("collection_mode_enabled") + + @property + def script_generator(self): + return self._script_generator + + @script_generator.setter + def script_generator(self, value): + self._script_generator = value + +# Example usage +if __name__ == "__main__": + def on_property_changed(name): + print(f"Property {name} changed") + + def on_static_property_changed(name): + print(f"Static property {name} changed") + + instrument_info = InstrumentInfo() + instrument_info.property_changed.append(on_property_changed) + InstrumentInfo.static_property_changed.append(on_static_property_changed) + + instrument_info.a2_label = "New A2 Setting:" + instrument_info.a2_choices = ["XLarge", "Large", "Medium", "Small"] + instrument_info.a2_trans_enabled = False + instrument_info.collection_mode_enabled = True +``` + +In this Python version: +- The + +InstrumentInfo + + class uses `tkinter` variable classes (`StringVar`, `BooleanVar`) for data binding. +- The `on_property_changed` method notifies subscribers when a property changes. +- The `on_static_property_changed` method notifies subscribers when a static property changes. +- Properties are defined for each attribute to get and set the values. +- An example usage is provided to demonstrate how to use the + +InstrumentInfo + + class and handle property changes. + +dehoni: Please translate the selected code into python and use approbriate packages. + +GitHub Copilot: Here is the translated code into Python: + +```python + + +class RunOrder: + ALL_TRANS = "All TRANS first" + TRANS_FIRST = "Alternate - TRANS first" + ALL_SANS = "All SANS first" + SANS_FIRST = "Alternate - SANS first" + + choices = [ + ALL_TRANS, + TRANS_FIRST, + ALL_SANS, + SANS_FIRST + ] + + @staticmethod + def get_choices(): + return RunOrder.choices + +# Example usage +if __name__ == "__main__": + print(RunOrder.get_choices()) +``` + +In this Python version: +- The + +RunOrder + + class is defined with class attributes for each run order type. +- The + +choices + + list contains all the run order types. +- The `get_choices` static method returns the list of choices. +- An example usage is provided to demonstrate how to access the choices. + +dehoni: Please translate the selected code into python and use approbriate packages. + +GitHub Copilot: Here is the translated code into Python: + +```python + + +class WaitForUnits: + MICROAMPS = "Microamp-hours" + MINUTES = "Minutes" + SECONDS = "Seconds" + FRAMES = "Frames" + + choices = [ + MICROAMPS, + MINUTES, + SECONDS, + FRAMES + ] + + @staticmethod + def get_choices(): + return WaitForUnits.choices + +# Example usage +if __name__ == "__main__": + print(WaitForUnits.get_choices()) +``` + +In this Python version: +- The + +WaitForUnits + + class is defined with class attributes for each wait unit type. +- The + +choices + + list contains all the wait unit types. +- The `get_choices` static method returns the list of choices. +- An example usage is provided to demonstrate how to access the choices. + +dehoni: Please translate the selected code into python and use approbriate packages. + +GitHub Copilot: Here is the translated code into Python: + +```python + + +import csv + +class CsvExporter: + @staticmethod + def export(file, runs): + with open(file, mode='w', newline='') as csvfile: + writer = csv.writer(csvfile) + for exp in runs: + row = [ + exp.position, + str(exp.trans), + exp.trans_wait, + str(exp.sans), + exp.sans_wait, + exp.period, + exp.sample, + exp.background, + exp.thickness, + exp.temperature1, + exp.temperature2, + exp.field, + exp.shear_rate1, + exp.shear_rate2, + exp.shear_angle1, + exp.shear_angle2, + exp.pre_command, + exp.post_command, + exp.rb_number + ] + writer.writerow(row) + +# Example usage +class Experiment: + def __init__(self, position="", trans=0, trans_wait="", sans=0, sans_wait="", period="", sample="", background="", thickness="", temperature1="", temperature2="", field="", shear_rate1="", shear_rate2="", shear_angle1="", shear_angle2="", pre_command="", post_command="", rb_number=""): + self.position = position + self.trans = trans + self.trans_wait = trans_wait + self.sans = sans + self.sans_wait = sans_wait + self.period = period + self.sample = sample + self.background = background + self.thickness = thickness + self.temperature1 = temperature1 + self.temperature2 = temperature2 + self.field = field + self.shear_rate1 = shear_rate1 + self.shear_rate2 = shear_rate2 + self.shear_angle1 = shear_angle1 + self.shear_angle2 = shear_angle2 + self.pre_command = pre_command + self.post_command = post_command + self.rb_number = rb_number + +if __name__ == "__main__": + runs = [ + Experiment(position="pos1", trans=10, trans_wait="Minutes", sans=20, sans_wait="Minutes", period="1", sample="Sample1", background="Background1", thickness="1.0", temperature1="25", temperature2="30", field="1.5T", shear_rate1="100", shear_rate2="200", shear_angle1="45", shear_angle2="90", pre_command="Pre Command", post_command="Post Command", rb_number="RB12345") + ] + CsvExporter.export('output.csv', runs) +``` + +In this Python version: +- The + +CsvExporter + + class uses the `csv` module to write data to a CSV file. +- The `export` method writes the properties of each + +Experiment + + object to the CSV file. +- An example + +Experiment + + class and usage are provided to demonstrate how to use the + +CsvExporter + + class. + +dehoni: Please translate the selected code into python and use approbriate packages. + +GitHub Copilot: Here is the translated code into Python using the `pandas` package for handling Excel files: + +```python + + +import pandas as pd +from tkinter import messagebox + +class ExcelIO: + columns = [ + "POSITION", "TRANS", "TRANS_WAIT_FOR", "SANS", "SANS_WAIT_FOR", "PERIOD", "SAMPLE_IDENTIFICATION_STRING", "BACKGROUND", + "THICKNESS", "TEMPERATURE", "TEMPERATURE2", "FIELD", "SHEAR_RATE_1", "SHEAR_RATE_2", "SHEAR_ANGLE_1", "SHEAR_ANGLE_2", + "PRE_COMMAND", "POST_COMMAND", "RB_NUMBER" + ] + + def __init__(self): + self.dataset = None + + def open_excel_workbook(self, filename): + try: + self.dataset = pd.read_excel(filename, sheet_name=None) + except Exception as ex: + messagebox.showerror("SANS Script - Error", str(ex)) + raise IOError("Could not open Excel spreadsheet") + + def extract_experiments(self): + experiments = [] + if self.dataset is not None: + for sheet_name, df in self.dataset.items(): + for _, row in df.iterrows(): + exp = Experiment( + position=row.get("POSITION", ""), + trans=self.cast_to_double(row.get("TRANS", 0)), + trans_wait=row.get("TRANS_WAIT_FOR", ""), + sans=self.cast_to_double(row.get("SANS", 0)), + sans_wait=row.get("SANS_WAIT_FOR", ""), + period=row.get("PERIOD", ""), + sample=row.get("SAMPLE_IDENTIFICATION_STRING", ""), + background=row.get("BACKGROUND", ""), + thickness=row.get("THICKNESS", ""), + temperature1=row.get("TEMPERATURE", ""), + temperature2=row.get("TEMPERATURE2", ""), + field=row.get("FIELD", ""), + shear_rate1=row.get("SHEAR_RATE_1", ""), + shear_rate2=row.get("SHEAR_RATE_2", ""), + shear_angle1=row.get("SHEAR_ANGLE_1", ""), + shear_angle2=row.get("SHEAR_ANGLE_2", ""), + pre_command=row.get("PRE_COMMAND", ""), + post_command=row.get("POST_COMMAND", ""), + rb_number=row.get("RB_NUMBER", "") + ) + experiments.append(exp) + return experiments + + @staticmethod + def cast_to_double(val): + try: + return float(val) + except (ValueError, TypeError): + return 0.0 + + def export_data_to_excel(self, runs, filename): + data = [] + for exp in runs: + data.append([ + exp.position, exp.trans, exp.trans_wait, exp.sans, exp.sans_wait, + exp.period, exp.sample, exp.background, exp.thickness, exp.temperature1, exp.temperature2, exp.field, + exp.shear_rate1, exp.shear_rate2, exp.shear_angle1, exp.shear_angle2, exp.pre_command, + exp.post_command, exp.rb_number + ]) + df = pd.DataFrame(data, columns=self.columns) + df.to_excel(filename, index=False) + +# Example usage +class Experiment: + def __init__(self, position="", trans=0, trans_wait="", sans=0, sans_wait="", period="", sample="", background="", thickness="", temperature1="", temperature2="", field="", shear_rate1="", shear_rate2="", shear_angle1="", shear_angle2="", pre_command="", post_command="", rb_number=""): + self.position = position + self.trans = trans + self.trans_wait = trans_wait + self.sans = sans + self.sans_wait = sans_wait + self.period = period + self.sample = sample + self.background = background + self.thickness = thickness + self.temperature1 = temperature1 + self.temperature2 = temperature2 + self.field = field + self.shear_rate1 = shear_rate1 + self.shear_rate2 = shear_rate2 + self.shear_angle1 = shear_angle1 + self.shear_angle2 = shear_angle2 + self.pre_command = pre_command + self.post_command = post_command + self.rb_number = rb_number + +if __name__ == "__main__": + runs = [ + Experiment(position="pos1", trans=10, trans_wait="Minutes", sans=20, sans_wait="Minutes", period="1", sample="Sample1", background="Background1", thickness="1.0", temperature1="25", temperature2="30", field="1.5T", shear_rate1="100", shear_rate2="200", shear_angle1="45", shear_angle2="90", pre_command="Pre Command", post_command="Post Command", rb_number="RB12345") + ] + excel_io = ExcelIO() + excel_io.export_data_to_excel(runs, 'output.xlsx') + excel_io.open_excel_workbook('output.xlsx') + experiments = excel_io.extract_experiments() + for exp in experiments: + print(exp.__dict__) +``` + +In this Python version: +- The + +ExcelIO + + class uses the `pandas` package to read and write Excel files. +- The `open_excel_workbook` method reads the Excel file into a `pandas` DataFrame. +- The `extract_experiments` method extracts experiment data from the DataFrame. +- The `export_data_to_excel` method writes experiment data to an Excel file. +- An example + +Experiment + + class and usage are provided to demonstrate how to use the + +ExcelIO + + class. + +dehoni: Please translate the selected code into python and use approbriate packages. + +GitHub Copilot: Here is the translated code into Python using the `tkinter` package for creating a similar dialog window: + +```python + + +import tkinter as tk +from tkinter import scrolledtext + +class PreviewDialog(tk.Toplevel): + def __init__(self, parent): + super().__init__(parent) + self.title("PreviewDialog") + self.geometry("600x500") + + self.grid_rowconfigure(0, weight=1) + self.grid_columnconfigure(0, weight=1) + + # Create a frame for the text area with a border + border_frame = tk.Frame(self, borderwidth=1, relief="solid") + border_frame.grid(row=0, column=0, padx=5, pady=5, sticky="nsew") + + # Create a scrollable text area + self.txt_script = scrolledtext.ScrolledText(border_frame, wrap=tk.WORD) + self.txt_script.pack(fill=tk.BOTH, expand=True) + + # Create a frame for the buttons + button_frame = tk.Frame(self) + button_frame.grid(row=1, column=0, pady=5, sticky="e") + + # Create the Print Script button (initially hidden) + self.btn_print = tk.Button(button_frame, text="Print Script", command=self.print_script) + self.btn_print.pack(side=tk.LEFT, padx=5) + self.btn_print.pack_forget() # Hide the button initially + + # Create the Close button + btn_close = tk.Button(button_frame, text="Close", command=self.close) + btn_close.pack(side=tk.LEFT, padx=5) + + def print_script(self): + # Implement the print functionality here + pass + + def close(self): + self.destroy() + +# Example usage +if __name__ == "__main__": + root = tk.Tk() + root.withdraw() # Hide the root window + + dialog = PreviewDialog(root) + dialog.txt_script.insert(tk.END, "This is a preview of the script.") + dialog.mainloop() +``` + +In this Python version: +- The `PreviewDialog` class is defined using the `tkinter` package. +- A `ScrolledText` widget is used to create a scrollable text area. +- A `Frame` widget is used to create a border around the text area. +- A `Frame` widget is used to create a horizontal stack of buttons. +- The `Print Script` button is initially hidden using the `pack_forget` method. +- The `close` method destroys the dialog window. +- An example usage is provided to demonstrate how to create and display the `PreviewDialog`. + +dehoni: Please translate the selected code into python and use approbriate packages. + +GitHub Copilot: Here is the translated code into Python using the `tkinter` package: + +```python + + +import tkinter as tk +from tkinter import scrolledtext + +class PreviewDialog(tk.Toplevel): + def __init__(self, parent): + super().__init__(parent) + self.title("PreviewDialog") + self.geometry("600x500") + + self.grid_rowconfigure(0, weight=1) + self.grid_columnconfigure(0, weight=1) + + # Create a frame for the text area with a border + border_frame = tk.Frame(self, borderwidth=1, relief="solid") + border_frame.grid(row=0, column=0, padx=5, pady=5, sticky="nsew") + + # Create a scrollable text area + self.txt_script = scrolledtext.ScrolledText(border_frame, wrap=tk.WORD) + self.txt_script.pack(fill=tk.BOTH, expand=True) + + # Create a frame for the buttons + button_frame = tk.Frame(self) + button_frame.grid(row=1, column=0, pady=5, sticky="e") + + # Create the Print Script button (initially hidden) + self.btn_print = tk.Button(button_frame, text="Print Script", command=self.print_script) + self.btn_print.pack(side=tk.LEFT, padx=5) + self.btn_print.pack_forget() # Hide the button initially + + # Create the Close button + btn_close = tk.Button(button_frame, text="Close", command=self.close) + btn_close.pack(side=tk.LEFT, padx=5) + + def set_script(self, script): + self.txt_script.delete(1.0, tk.END) + self.txt_script.insert(tk.END, script) + + def print_script(self): + # Implement the print functionality here + pass + + def close(self): + self.destroy() + +# Example usage +if __name__ == "__main__": + root = tk.Tk() + root.withdraw() # Hide the root window + + dialog = PreviewDialog(root) + dialog.set_script("This is a preview of the script.") + dialog.mainloop() +``` + +In this Python version: +- The + +PreviewDialog + + class is defined using the `tkinter` package. +- The `set_script` method sets the text of the `ScrolledText` widget. +- The `print_script` method is a placeholder for the print functionality. +- The `close` method destroys the dialog window. +- An example usage is provided to demonstrate how to create and display the + +PreviewDialog + +. \ No newline at end of file diff --git a/ViewModels/A2Setting.py b/ViewModels/A2Setting.py new file mode 100644 index 0000000..773a782 --- /dev/null +++ b/ViewModels/A2Setting.py @@ -0,0 +1,6 @@ +class A2Setting: + Large = "Large" + Medium = "Medium" + Small = "Small" + XLarge = "XLarge" + Larmor = "Larmor" \ No newline at end of file diff --git a/ViewModels/DataCollection.py b/ViewModels/DataCollection.py new file mode 100644 index 0000000..40ce416 --- /dev/null +++ b/ViewModels/DataCollection.py @@ -0,0 +1,4 @@ +class DataCollection: + @staticmethod + def choices(): + return ["Mode1", "Mode2", "Mode3"] \ No newline at end of file diff --git a/ViewModels/DataGridVM.cs b/ViewModels/DataGridVM.cs new file mode 100644 index 0000000..7dd4481 --- /dev/null +++ b/ViewModels/DataGridVM.cs @@ -0,0 +1,41 @@ +using System.Collections.ObjectModel; +using System.ComponentModel; + +namespace LOQ_Script_Gui +{ + class DataGridVM : INotifyPropertyChanged + { + public event PropertyChangedEventHandler PropertyChanged; + + protected void OnPropertyChanged(string name) + { + PropertyChangedEventHandler handler = PropertyChanged; + if (handler != null) + { + handler(this, new PropertyChangedEventArgs(name)); + } + } + + ObservableCollection runs = new ObservableCollection(); + public ObservableCollection Runs + { + get + { + return runs; + } + set + { + runs = value; + OnPropertyChanged("Runs"); + } + } + + public static ObservableCollection WaitForChoices + { + get + { + return WaitForUnits.Choices; + } + } + } +} diff --git a/ViewModels/DataGridVM.py b/ViewModels/DataGridVM.py new file mode 100644 index 0000000..0b9dfc7 --- /dev/null +++ b/ViewModels/DataGridVM.py @@ -0,0 +1,47 @@ +import tkinter as tk +from tkinter import StringVar +from tkinter import ttk +from Models.WaitForUnits import WaitForUnits +from Models.Experiment import Experiment + + + +class DataGridVM: + def __init__(self): + self._runs = tk.Variable(value=[]) + self.property_changed = [] + + @property + def runs(self): + return self._runs.get() + + @runs.setter + def runs(self, value): + self._runs.set(value) + self.on_property_changed("runs") + + @staticmethod + def wait_for_choices(): + return WaitForUnits.choices() + + def on_property_changed(self, name): + for callback in self.property_changed: + callback(name) + + + + + +if __name__ == "__main__": + root = tk.Tk() + vm = DataGridVM() + + def on_property_changed(name): + print(f"Property {name} changed") + + vm.property_changed.append(on_property_changed) + + vm.runs = [Experiment(), Experiment()] + print(vm.runs) + + root.mainloop() \ No newline at end of file diff --git a/ViewModels/EstimationPanelVM.cs b/ViewModels/EstimationPanelVM.cs new file mode 100644 index 0000000..441ae08 --- /dev/null +++ b/ViewModels/EstimationPanelVM.cs @@ -0,0 +1,24 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Collections.ObjectModel; +using System.ComponentModel; + +namespace LOQ_Script_Gui +{ + class EstimationPanelVM : INotifyPropertyChanged + { + public event PropertyChangedEventHandler PropertyChanged; + + protected void OnPropertyChanged(string name) + { + PropertyChangedEventHandler handler = PropertyChanged; + if (handler != null) + { + handler(this, new PropertyChangedEventArgs(name)); + } + } + } +} diff --git a/ViewModels/EstimationPanelVM.py b/ViewModels/EstimationPanelVM.py new file mode 100644 index 0000000..d9e2c76 --- /dev/null +++ b/ViewModels/EstimationPanelVM.py @@ -0,0 +1,28 @@ +import tkinter as tk + + + +class EstimationPanelVM: + def __init__(self): + self.property_changed = [] + self.count_rate = tk.StringVar() + self.move_time = tk.StringVar() + self.script_time = tk.StringVar() + + def on_property_changed(self, name): + for callback in self.property_changed: + callback(name) + +if __name__ == "__main__": + root = tk.Tk() + vm = EstimationPanelVM() + + def on_property_changed(name): + print(f"Property {name} changed") + + vm.property_changed.append(on_property_changed) + + # Example of triggering property change + vm.on_property_changed("example_property") + + root.mainloop() \ No newline at end of file diff --git a/ViewModels/Geometry.py b/ViewModels/Geometry.py new file mode 100644 index 0000000..bdc6546 --- /dev/null +++ b/ViewModels/Geometry.py @@ -0,0 +1,4 @@ +class Geometry: + @staticmethod + def choices(): + return ["Geometry1", "Geometry2", "Geometry3"] \ No newline at end of file diff --git a/ViewModels/InstrumentVM.cs b/ViewModels/InstrumentVM.cs new file mode 100644 index 0000000..84daf86 --- /dev/null +++ b/ViewModels/InstrumentVM.cs @@ -0,0 +1,179 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Collections.ObjectModel; +using System.ComponentModel; + +namespace LOQ_Script_Gui +{ + class InstrumentVM : INotifyPropertyChanged + { + List loqApertureSizes = new List() + { + A2Setting.Large, A2Setting.Medium, A2Setting.Small + }; + + List sans2dApertureSizes = new List() + { + A2Setting.XLarge, A2Setting.Large, A2Setting.Medium, A2Setting.Small + }; + + List larmorApertureSizes = new List() + { + A2Setting.Larmor + //A2Setting.LarmorLarge, A2Setting.LarmorMedium, A2Setting.LarmorSmall + }; + + List zoomApertureSizes = new List() + { + A2Setting.Large, A2Setting.Medium, A2Setting.Small + }; + + public event PropertyChangedEventHandler PropertyChanged; + + protected void OnPropertyChanged(string name) + { + PropertyChangedEventHandler handler = PropertyChanged; + if (handler != null) + { + handler(this, new PropertyChangedEventArgs(name)); + } + } + + private bool isLarmor = true; + public bool IsLarmor + { + get + { + return isLarmor; + } + set + { + isLarmor = value; + if (value) + { + IsLoq = false; + IsSans2d = false; + IsZoom = false; + InstrumentInfo.ScriptGenerator = new Scripting.GeniePythonScriptGenerator(true,false,false,false); + InstrumentInfo.A2Label = "A1, S1 Setting:"; + InstrumentInfo.A2Choices = new ObservableCollection(larmorApertureSizes); + InstrumentInfo.A2TransEnabled = true; + InstrumentInfo.CollectionModeEnabled = true; + estimation.CountRate = 40; + //settings.A2SettingSans = A2Setting.LarmorMedium; + settings.A2SettingSans = Properties.Settings.Default.A2SettingSans; + //settings.A2SettingTrans = A2Setting.LarmorMedium; + settings.A2SettingTrans = Properties.Settings.Default.A2SettingTrans; + } + + OnPropertyChanged("IsLarmor"); + } + } + + private bool isLoq = false; + public bool IsLoq + { + get + { + return isLoq; + } + set + { + isLoq = value; + if (value) + { + IsLarmor = false; + IsSans2d = false; + IsZoom = false; + InstrumentInfo.ScriptGenerator = new Scripting.GeniePythonScriptGenerator(false,true,false,false); + InstrumentInfo.A2Label = "A2 Setting:"; + InstrumentInfo.A2Choices = new ObservableCollection(loqApertureSizes); + InstrumentInfo.A2TransEnabled = true; + InstrumentInfo.CollectionModeEnabled = true; + estimation.CountRate = 170; + //settings.A2SettingSans = A2Setting.Large; + settings.A2SettingSans = Properties.Settings.Default.A2SettingSans; + //settings.A2SettingTrans = A2Setting.Large; + settings.A2SettingTrans = Properties.Settings.Default.A2SettingTrans; + } + + OnPropertyChanged("IsLoq"); + } + } + + private bool isSans2d = false; + public bool IsSans2d + { + get + { + return isSans2d; + } + set + { + isSans2d = value; + if (value) + { + IsLarmor = false; + IsLoq = false; + IsZoom = false; + //InstrumentInfo.ScriptGenerator = new Scripting.OpenGenieScriptGenerator(true); + InstrumentInfo.ScriptGenerator = new Scripting.GeniePythonScriptGenerator(false, false, false, true); + InstrumentInfo.A2Label = "A6 Setting:"; + InstrumentInfo.A2Choices = new ObservableCollection(sans2dApertureSizes); + InstrumentInfo.A2TransEnabled = true; + InstrumentInfo.CollectionModeEnabled = true; + estimation.CountRate = 40; + //settings.A2SettingSans = A2Setting.Large; + settings.A2SettingSans = Properties.Settings.Default.A2SettingSans; + //settings.A2SettingTrans = A2Setting.Large; + settings.A2SettingTrans = Properties.Settings.Default.A2SettingTrans; + } + + OnPropertyChanged("IsSans2d"); + } + } + + private bool isZoom = false; + public bool IsZoom + { + get + { + return isZoom; + } + set + { + isZoom = value; + if (value) + { + IsLarmor = false; + IsLoq = false; + IsSans2d = false; + InstrumentInfo.ScriptGenerator = new Scripting.GeniePythonScriptGenerator(false,false,true,false); + InstrumentInfo.A2Label = "A6 Setting:"; + InstrumentInfo.A2Choices = new ObservableCollection(zoomApertureSizes); + InstrumentInfo.A2TransEnabled = true; + InstrumentInfo.CollectionModeEnabled = true; + estimation.CountRate = 40; + //settings.A2SettingSans = A2Setting.Large; + settings.A2SettingSans = Properties.Settings.Default.A2SettingSans; + //settings.A2SettingTrans = A2Setting.Large; + settings.A2SettingTrans = Properties.Settings.Default.A2SettingTrans; + } + + OnPropertyChanged("IsZoom"); + } + } + + ExperimentSettings settings; + Estimation estimation; + + public InstrumentVM(ExperimentSettings settings, Estimation estimation) + { + this.settings = settings; + this.estimation = estimation; + } + } +} diff --git a/ViewModels/InstrumentVM.py b/ViewModels/InstrumentVM.py new file mode 100644 index 0000000..49fb38a --- /dev/null +++ b/ViewModels/InstrumentVM.py @@ -0,0 +1,87 @@ +import tkinter as tk +from tkinter import StringVar, BooleanVar +from Models.A2Setting import A2Setting +from Models.InstrumentInfo import InstrumentInfo +from Models.Estimation import Estimation +from Models.ExperimentSettings import ExperimentSettings +from Scripting.GeniePythonScriptGenerator import GeniePythonScriptGenerator +from Properties.Settings import Settings + + +class InstrumentVM: + def __init__(self, settings, estimation): + #def __init__(self, experiment, estimate): + self.settings = settings + self.estimation = estimation + #self.estimate = estimate + #self.experiment = experiment + + + self.loq_aperture_sizes = [A2Setting.LARGE, A2Setting.MEDIUM, A2Setting.SMALL] + self.sans2d_aperture_sizes = [A2Setting.X_LARGE, A2Setting.LARGE, A2Setting.MEDIUM, A2Setting.SMALL] + self.larmor_aperture_sizes = [A2Setting.LARMOR_LARGE,A2Setting.LARMOR_MEDIUM,A2Setting.LARMOR_SMALL] + self.zoom_aperture_sizes = [A2Setting.LARGE, A2Setting.MEDIUM, A2Setting.SMALL] + + self.is_larmor = BooleanVar(value=True) + self.is_loq = BooleanVar(value=False) + self.is_sans2d = BooleanVar(value=False) + self.is_zoom = BooleanVar(value=False) + ''' + self.is_larmor = False + self.is_loq = False + self.is_sans2d = False + self.is_zoom = False + ''' + + self.is_larmor.trace_add("write", self.update_instrument) + self.is_loq.trace_add("write", self.update_instrument) + self.is_sans2d.trace_add("write", self.update_instrument) + self.is_zoom.trace_add("write", self.update_instrument) + + self.update_instrument() + + def update_instrument(self, *args): + if self.is_larmor.get(): + self.set_instrument(True, False, False, False, "A1, S1 Setting:", self.larmor_aperture_sizes, 40) + elif self.is_loq.get(): + self.set_instrument(False, True, False, False, "A2 Setting:", self.loq_aperture_sizes, 170) + elif self.is_sans2d.get(): + self.set_instrument(False, False, True, False, "A6 Setting:", self.sans2d_aperture_sizes, 40) + elif self.is_zoom.get(): + self.set_instrument(False, False, False, True, "A6 Setting:", self.zoom_aperture_sizes, 40) + + def set_instrument(self, larmor, loq, sans2d, zoom, a2_label, a2_choices, count_rate): + self.is_larmor.set(larmor) + self.is_loq.set(loq) + self.is_sans2d.set(sans2d) + self.is_zoom.set(zoom) + + InstrumentInfo.script_generator = GeniePythonScriptGenerator(larmor, loq, zoom, sans2d) + + InstrumentInfo.a2_label = a2_label + InstrumentInfo.a2_choices = a2_choices + InstrumentInfo.a2_trans_enabled = True + InstrumentInfo.collection_mode_enabled = True + self.estimation.count_rate = count_rate + self.settings=Settings() + self.settings.a2_setting_sans = Settings.a2_setting_sans + self.settings.a2_setting_trans = Settings.a2_setting_trans + + + + +if __name__ == "__main__": + root = tk.Tk() + settings = ExperimentSettings() + estimation = Estimation() + vm = InstrumentVM(settings, estimation) + + def on_property_changed(name): + print(f"Property {name} changed") + + vm.is_larmor.trace_add("write", lambda *args: on_property_changed("is_larmor")) + vm.is_loq.trace_add("write", lambda *args: on_property_changed("is_loq")) + vm.is_sans2d.trace_add("write", lambda *args: on_property_changed("is_sans2d")) + vm.is_zoom.trace_add("write", lambda *args: on_property_changed("is_zoom")) + + root.mainloop() \ No newline at end of file diff --git a/ViewModels/RunOrder.py b/ViewModels/RunOrder.py new file mode 100644 index 0000000..bec23ec --- /dev/null +++ b/ViewModels/RunOrder.py @@ -0,0 +1,4 @@ +class RunOrder: + @staticmethod + def choices(): + return ["Choice1", "Choice2", "Choice3"] \ No newline at end of file diff --git a/ViewModels/SettingsPanelVM.cs b/ViewModels/SettingsPanelVM.cs new file mode 100644 index 0000000..d3b6b2d --- /dev/null +++ b/ViewModels/SettingsPanelVM.cs @@ -0,0 +1,87 @@ +using System.Collections.ObjectModel; +using System.ComponentModel; +using System; + +namespace LOQ_Script_Gui +{ + class SettingsPanelVM : INotifyPropertyChanged + { + public event PropertyChangedEventHandler PropertyChanged; + + protected void OnPropertyChanged(string name) + { + PropertyChangedEventHandler handler = PropertyChanged; + if (handler != null) + { + handler(this, new PropertyChangedEventArgs(name)); + } + } + + // Provides the combo box options + public static ObservableCollection OrderChoices + { + get + { + return RunOrder.Choices; + } + } + + // Provides the combo box options + public static ObservableCollection GeometryChoices + { + get + { + return Geometry.Choices; + } + } + + // Provides the combo box options + public static ObservableCollection CollectionModeChoices + { + get + { + return DataCollection.Choices; + } + } + + ExperimentSettings experiment = new ExperimentSettings(); + public ExperimentSettings Experiment + { + get + { + return experiment; + } + set + { + experiment = value; + OnPropertyChanged("Experiment"); + } + } + + InstrumentInfo instInfo; + public InstrumentInfo InstInfo + { + get + { + return instInfo; + } + set + { + instInfo = value; + OnPropertyChanged("InstInfo"); + } + } + + public SettingsPanelVM() + { + // Need a default constructor to keep the databinding happy! + // The other constructor should be called ASAP + instInfo = new InstrumentInfo(); + } + + public SettingsPanelVM(InstrumentInfo instInfo) + { + this.instInfo = instInfo; + } + } +} diff --git a/ViewModels/SettingsPanelVM.py b/ViewModels/SettingsPanelVM.py new file mode 100644 index 0000000..a5a5a2a --- /dev/null +++ b/ViewModels/SettingsPanelVM.py @@ -0,0 +1,75 @@ +import tkinter as tk +from tkinter import StringVar +from Models.Estimation import Estimation +from Models.Experiment import Experiment +from Models.ExperimentSettings import ExperimentSettings +from Models.InstrumentInfo import InstrumentInfo +from Models.DataCollection import DataCollection +from Models.RunOrder import RunOrder +from Models.Geometry import Geometry + +class SettingsPanelVM: + def __init__(self, inst_info=None): + self.property_changed = [] + self.order_choices = ["Choice1", "Choice2", "Choice3"] + self.geometry_choices = ["Geometry1", "Geometry2", "Geometry3"] + + + self._experiment = ExperimentSettings() + self._inst_info = inst_info if inst_info is not None else InstrumentInfo() + + def on_property_changed(self, name): + for callback in self.property_changed: + callback(name) + + @property + def experiment(self): + return self._experiment + + @experiment.setter + def experiment(self, value): + self._experiment = value + self.on_property_changed("experiment") + + @property + def inst_info(self): + return self._inst_info + + @inst_info.setter + def inst_info(self, value): + self._inst_info = value + self.on_property_changed("inst_info") + + @staticmethod + def order_choices(): + return RunOrder.choices() + + @staticmethod + def geometry_choices(): + return Geometry.choices() + + @staticmethod + def collection_mode_choices(): + return DataCollection.choices() + + + + + + + + + +if __name__ == "__main__": + root = tk.Tk() + vm = SettingsPanelVM() + + def on_property_changed(name): + print(f"Property {name} changed") + + vm.property_changed.append(on_property_changed) + + vm.experiment = ExperimentSettings() + vm.inst_info = InstrumentInfo() + + root.mainloop() \ No newline at end of file diff --git a/ViewModels/WaitForUnits.py b/ViewModels/WaitForUnits.py new file mode 100644 index 0000000..69c86ba --- /dev/null +++ b/ViewModels/WaitForUnits.py @@ -0,0 +1,4 @@ +class WaitForUnits: + @staticmethod + def choices(): + return ["Choice1", "Choice2", "Choice3"] \ No newline at end of file diff --git a/app_config.ini b/app_config.ini new file mode 100644 index 0000000..1e228bb --- /dev/null +++ b/app_config.ini @@ -0,0 +1,13 @@ +[DEFAULT] +LastInstrument = 'LARMOR' +SampleHeight = '11' +SampleWidth = '11' +Geometry = 'Flat Plate' +RunOrder = 'Alternate - TRANS first' +NumTrans = '1' +NumSans = '1' +LoopPerRun = 'False' +A2SettingSans = 'Large = 25, 16 mm' +A2SettingTrans = 'Large = 25, 16 mm' +CollectionMode = 'Events' + From 5206abadf4d4d4440b28661dc22953a212edbd4e Mon Sep 17 00:00:00 2001 From: Dirk Honecker Date: Sun, 15 Dec 2024 15:15:43 +0000 Subject: [PATCH 03/88] radiobutton for instrument selection --- App.py | 4 +++- Dialogs/PreviewDialog.py | 6 +++--- EstimationPanel.py | 4 +++- MainWindow.py | 43 ++++++++++++++++++---------------------- Properties/Settings.ini | 4 ++-- SettingsPanel.py | 31 ++++++++++++++++------------- 6 files changed, 47 insertions(+), 45 deletions(-) diff --git a/App.py b/App.py index 86d8cb9..a2ba1c5 100644 --- a/App.py +++ b/App.py @@ -5,7 +5,9 @@ class App: def __init__(self): self.root = tk.Tk() self.main_window = MainWindow(self.root) - self.main_window.pack(fill=tk.BOTH, expand=True) + self.main_window.grid(sticky="nsew") + self.root.grid_rowconfigure(0, weight=1) + self.root.grid_columnconfigure(0, weight=1) def run(self): self.root.mainloop() diff --git a/Dialogs/PreviewDialog.py b/Dialogs/PreviewDialog.py index 167bcb8..4138fa3 100644 --- a/Dialogs/PreviewDialog.py +++ b/Dialogs/PreviewDialog.py @@ -18,7 +18,7 @@ def __init__(self, parent): # Create a scrollable text area self.txt_script = scrolledtext.ScrolledText(border_frame, wrap=tk.WORD) - self.txt_script.pack(fill=tk.BOTH, expand=True) + self.txt_script.grid(row=0, column=0, sticky="nsew") self.script_text.insert(tk.END, self.txt_script) self.script_text.config(state=tk.DISABLED) @@ -28,12 +28,12 @@ def __init__(self, parent): # Create the Print Script button (initially hidden) self.btn_print = tk.Button(button_frame, text="Print Script", command=self.print_script) - self.btn_print.pack(side=tk.LEFT, padx=5) + self.btn_print.grid(row=0, column=0, padx=5) self.btn_print.pack_forget() # Hide the button initially # Create the Close button btn_close = tk.Button(button_frame, text="Close", command=self.close) - btn_close.pack(side=tk.LEFT, padx=5) + btn_close.grid(row=0, column=1, padx=5) def set_script(self, script): self.txt_script.delete(1.0, tk.END) diff --git a/EstimationPanel.py b/EstimationPanel.py index 5f81c01..6372d99 100644 --- a/EstimationPanel.py +++ b/EstimationPanel.py @@ -58,5 +58,7 @@ def btn_calculate_click(self): root = tk.Tk() estimate = EstimationPanelVM() panel = EstimationPanel(root, estimate) - panel.pack(fill=tk.BOTH, expand=True) + panel.grid(sticky="nsew") + root.grid_rowconfigure(0, weight=1) + root.grid_columnconfigure(0, weight=1) root.mainloop() \ No newline at end of file diff --git a/MainWindow.py b/MainWindow.py index 9f064e1..435c73a 100644 --- a/MainWindow.py +++ b/MainWindow.py @@ -41,11 +41,18 @@ def create_widgets(self): - #self.pnl_settings.pack(fill=tk.X, expand=True, padx=5, pady=5) - # Data Grid - self.data_grid = ttk.Treeview(self, columns=("POSITION", "TRANS", "TRANS__WAIT", "SANS", "SANS__WAIT", "PERIOD", "SAMPLE__IDENTIFICATION__STRING", "BACKGROUND", "THICKNESS", "TEMPERATURE", "TEMPERATURE2", "FIELD", "SHEAR__RATE__1", "SHEAR__RATE__2", "SHEAR__ANGLE__1", "SHEAR__ANGLE__2", "PRE__COMMAND", "POST__COMMAND", "RB__NUMBER"), show="headings") - #self.pnl_settings.pack(fill=tk.X, expand=True, padx=5, pady=5) - self.data_grid.grid(row=0, column=1, rowspan=4, sticky="nsew", padx=5, pady=5) + + # Data Grid with horizontal scrollbar + data_frame = ttk.Frame(self) + data_frame.grid(row=0, column=1, rowspan=4, sticky="nsew", padx=5, pady=5) + + self.data_grid = ttk.Treeview(data_frame, columns=("POSITION", "TRANS", "TRANS__WAIT", "SANS", "SANS__WAIT", "PERIOD", "SAMPLE__IDENTIFICATION__STRING", "BACKGROUND", "THICKNESS", "TEMPERATURE", "TEMPERATURE2", "FIELD", "SHEAR__RATE__1", "SHEAR__RATE__2", "SHEAR__ANGLE__1", "SHEAR__ANGLE__2", "PRE__COMMAND", "POST__COMMAND", "RB__NUMBER"), show="headings") + + self.data_grid.grid(row=0, column=0, sticky="nsew") + + scrollbar = ttk.Scrollbar(data_frame, orient=tk.HORIZONTAL, command=self.data_grid.xview) + scrollbar.grid(row=1, column=0, sticky="ns") + self.data_grid.configure(xscrollcommand=scrollbar.set) for col in self.data_grid["columns"]: self.data_grid.heading(col, text=col.replace("__", " ")) # Settings Panel @@ -81,14 +88,11 @@ def create_widgets(self): instrument_menu = tk.Menu(self.mnu_main, tearoff=0) self.mnu_main.add_cascade(label="Instrument", menu=instrument_menu) - self.is_larmor = tk.BooleanVar() - self.is_loq = tk.BooleanVar() - self.is_sans2d = tk.BooleanVar() - self.is_zoom = tk.BooleanVar() - instrument_menu.add_checkbutton(label="LARMOR", variable=self.is_larmor) - instrument_menu.add_checkbutton(label="LOQ", variable=self.is_loq) - instrument_menu.add_checkbutton(label="SANS2D", variable=self.is_sans2d) - instrument_menu.add_checkbutton(label="ZOOM", variable=self.is_zoom) + self.instrument_var = tk.StringVar(value="LARMOR") + instrument_menu.add_radiobutton(label="LARMOR", variable=self.instrument_var, value="LARMOR") + instrument_menu.add_radiobutton(label="LOQ", variable=self.instrument_var, value="LOQ") + instrument_menu.add_radiobutton(label="SANS2D", variable=self.instrument_var, value="SANS2D") + instrument_menu.add_radiobutton(label="ZOOM", variable=self.instrument_var, value="ZOOM") @@ -98,19 +102,10 @@ def create_widgets(self): def load_last_instrument(self): inst = "LARMOR" # Replace with actual settings retrieval - if inst == "LARMOR": - self.instrument.is_larmor = True - elif inst == "LOQ": - self.instrument.is_loq = True - elif inst == "SANS2D": - self.instrument.is_sans2d = True - elif inst == "ZOOM": - self.instrument.is_zoom = True - else: - self.instrument.is_larmor = True + self.instrument_var.set(inst) def pnl_settings_on_write_clicked(self): - filetypes = [("genie_python Files", "*.py")] if self.instrument.is_larmor or self.instrument.is_loq or self.instrument.is_zoom or self.instrument.is_sans2d else [("Open Genie Files", "*.gcl")] + filetypes = [("genie_python Files", "*.py")] if self.instrument_var.get() in ["LARMOR", "LOQ", "ZOOM", "SANS2D"] else [("Open Genie Files", "*.gcl")] filepath = filedialog.asksaveasfilename(defaultextension=filetypes[0][1], filetypes=filetypes) if filepath: scriptname = filepath.split('/')[-1].replace(" ", "_").rsplit('.', 1)[0] diff --git a/Properties/Settings.ini b/Properties/Settings.ini index 76b9093..8e8260a 100644 --- a/Properties/Settings.ini +++ b/Properties/Settings.ini @@ -7,7 +7,7 @@ runorder = 'Alternate - TRANS first' numtrans = '1' numsans = '1' loopperrun = 'False' -a2settingsans = -a2settingtrans = +a2settingsans = +a2settingtrans = collectionmode = 'Events' diff --git a/SettingsPanel.py b/SettingsPanel.py index 2938fed..76b1f91 100644 --- a/SettingsPanel.py +++ b/SettingsPanel.py @@ -21,10 +21,11 @@ def create_widgets(self): label = ttk.Label(self, text="Settings", font=("Helvetica", 16, "bold")) - label.pack(pady=10) + label.grid(row=0, column=0, columnspan=2, pady=10) grid_frame = ttk.Frame(self) - grid_frame.pack(padx=10, pady=10) + grid_frame.grid(row=1, column=0, columnspan=2, padx=10, pady=10, sticky="nsew") + # Order ttk.Label(grid_frame, text="Order:").grid(row=0, column=0, sticky=tk.E) @@ -36,16 +37,16 @@ def create_widgets(self): sans_frame = ttk.Frame(grid_frame) sans_frame.grid(row=1, column=1, columnspan=2) self.txt_times_sans = ttk.Entry(sans_frame, width=5) - self.txt_times_sans.pack(side=tk.LEFT, padx=5, pady=5) - ttk.Label(sans_frame, text="time(s)").pack(side=tk.LEFT) + self.txt_times_sans.grid(row=0, column=0, padx=5, pady=5) + ttk.Label(sans_frame, text="time(s)").grid(row=0, column=1) # Do TRANS ttk.Label(grid_frame, text="Do TRANS:").grid(row=2, column=0, sticky=tk.E) trans_frame = ttk.Frame(grid_frame) trans_frame.grid(row=2, column=1, columnspan=2) self.txt_times_trans = ttk.Entry(trans_frame, width=5) - self.txt_times_trans.pack(side=tk.LEFT, padx=5, pady=5) - ttk.Label(trans_frame, text="time(s)").pack(side=tk.LEFT) + self.txt_times_trans.grid(row=0, column=0, padx=5, pady=5) + ttk.Label(trans_frame, text="time(s)").grid(row=0, column=1) # Loop over each run self.chk_loop_run = ttk.Checkbutton(grid_frame, text="Loop over each run?") @@ -61,24 +62,24 @@ def create_widgets(self): sample_height_frame = ttk.Frame(grid_frame) sample_height_frame.grid(row=7, column=1, columnspan=2) self.txt_sample_height = ttk.Entry(sample_height_frame, width=5) - self.txt_sample_height.pack(side=tk.LEFT, padx=5, pady=5) - ttk.Label(sample_height_frame, text="mm").pack(side=tk.LEFT) + self.txt_sample_height.grid(row=0, column=0, padx=5, pady=5) + ttk.Label(sample_height_frame, text="mm").grid(row=0, column=1) # Sample Width ttk.Label(grid_frame, text="Sample Width:").grid(row=8, column=0, sticky=tk.E) sample_width_frame = ttk.Frame(grid_frame) sample_width_frame.grid(row=8, column=1, columnspan=2) self.txt_sample_width = ttk.Entry(sample_width_frame, width=5) - self.txt_sample_width.pack(side=tk.LEFT, padx=5, pady=5) - ttk.Label(sample_width_frame, text="mm").pack(side=tk.LEFT) + self.txt_sample_width.grid(row=0, column=0, padx=5, pady=5) + ttk.Label(sample_width_frame, text="mm").grid(row=0, column=1) # Buttons button_frame = ttk.Frame(self) - button_frame.pack(pady=10) + button_frame.grid(row=2, column=0, columnspan=2, pady=10) self.btn_preview_script = ttk.Button(button_frame, text="Preview Script", command=self.btn_preview_script_click) - self.btn_preview_script.pack(side=tk.LEFT, padx=5) + self.btn_preview_script.grid(row=0, column=0, padx=5) self.btn_write_script = ttk.Button(button_frame, text="Write Script", command=self.btn_write_script_click) - self.btn_write_script.pack(side=tk.LEFT, padx=5) + self.btn_write_script.grid(row=0, column=1, padx=5) def btn_preview_script_click(self): if self.on_preview_clicked: @@ -94,5 +95,7 @@ def btn_write_script_click(self): if __name__ == "__main__": root = tk.Tk() panel = SettingsPanel(root) - panel.pack() + panel.grid(sticky="nsew") + root.grid_rowconfigure(0, weight=1) + root.grid_columnconfigure(0, weight=1) root.mainloop() \ No newline at end of file From 96cd00ad418d6c1d44e6edfe61974017118dc07f Mon Sep 17 00:00:00 2001 From: Dirk Honecker Date: Sun, 22 Dec 2024 06:10:28 +0000 Subject: [PATCH 04/88] Setting up repository --- App.py | 4 +- MainWindow.py | 58 ++-- Models/InstrumentInfo.py | 4 +- Properties/Settings.ini | 4 +- SANS_Script_GUI_v4.csproj.py | 3 +- Scripting/IScriptGenerator.py | 10 - Scripting/OpenGenieScriptGenerator.py | 309 ------------------ ...nScriptGenerator.py => ScriptGenerator.py} | 8 +- SettingsPanel.py | 1 + ViewModels/DataCollection.py | 4 - ViewModels/Geometry.py | 4 - ViewModels/InstrumentVM.py | 4 +- ViewModels/RunOrder.py | 4 - ViewModels/SettingsPanelVM.py | 14 +- 14 files changed, 57 insertions(+), 374 deletions(-) delete mode 100644 Scripting/IScriptGenerator.py delete mode 100644 Scripting/OpenGenieScriptGenerator.py rename Scripting/{GeniePythonScriptGenerator.py => ScriptGenerator.py} (97%) delete mode 100644 ViewModels/DataCollection.py delete mode 100644 ViewModels/Geometry.py delete mode 100644 ViewModels/RunOrder.py diff --git a/App.py b/App.py index a2ba1c5..27e06fe 100644 --- a/App.py +++ b/App.py @@ -6,8 +6,8 @@ def __init__(self): self.root = tk.Tk() self.main_window = MainWindow(self.root) self.main_window.grid(sticky="nsew") - self.root.grid_rowconfigure(0, weight=1) - self.root.grid_columnconfigure(0, weight=1) + self.root.grid_rowconfigure(1, weight=1) + self.root.grid_columnconfigure(1, weight=1) def run(self): self.root.mainloop() diff --git a/MainWindow.py b/MainWindow.py index 435c73a..bde4c06 100644 --- a/MainWindow.py +++ b/MainWindow.py @@ -3,7 +3,6 @@ from tkinter.scrolledtext import ScrolledText import pandas as pd from Models.DataCollection import DataCollection -from Models.RunOrder import RunOrder from ViewModels.DataGridVM import DataGridVM from ViewModels.SettingsPanelVM import SettingsPanelVM from ViewModels.InstrumentVM import InstrumentVM @@ -35,26 +34,44 @@ def __init__(self,parent): def create_widgets(self): #self.data_grid = ttk.Treeview(self) self.grid_columnconfigure(1, weight=1) - self.grid_rowconfigure(0, weight=1) + self.grid_rowconfigure(1, weight=1) - # Data Grid with horizontal scrollbar - data_frame = ttk.Frame(self) - data_frame.grid(row=0, column=1, rowspan=4, sticky="nsew", padx=5, pady=5) - - self.data_grid = ttk.Treeview(data_frame, columns=("POSITION", "TRANS", "TRANS__WAIT", "SANS", "SANS__WAIT", "PERIOD", "SAMPLE__IDENTIFICATION__STRING", "BACKGROUND", "THICKNESS", "TEMPERATURE", "TEMPERATURE2", "FIELD", "SHEAR__RATE__1", "SHEAR__RATE__2", "SHEAR__ANGLE__1", "SHEAR__ANGLE__2", "PRE__COMMAND", "POST__COMMAND", "RB__NUMBER"), show="headings") + # Data Grid with horizontal and vertical scrollbars - self.data_grid.grid(row=0, column=0, sticky="nsew") + + ''' + self.canvas = tk.Canvas(self, borderwidth=0, background="#ffffff") + self.data_frame = tk.Frame(self.canvas, background="#f0ffff") + #data_frame = ttk.Frame(self) + self.scrolly = tk.Scrollbar(self, orient="vertical", command=self.canvas.yview) + self.scrollx = tk.Scrollbar(self, orient="horizontal", command=self.canvas.xview) + self.canvas.configure(xscrollcommand=self.scrollx.set) + self.canvas.configure(yscrollcommand=self.scrolly.set) + self.canvas.create_window((4,4), window=self.data_frame, anchor="nw", tags="self.frame") + + self.data_frame.bind("", self.onFrameConfigure) + ''' - scrollbar = ttk.Scrollbar(data_frame, orient=tk.HORIZONTAL, command=self.data_grid.xview) - scrollbar.grid(row=1, column=0, sticky="ns") - self.data_grid.configure(xscrollcommand=scrollbar.set) + self.data_frame = ttk.Frame(self) + self.data_frame.grid(row=0, column=1, rowspan=4, sticky="nsew", padx=5, pady=5) + self.data_grid = ttk.Treeview(self.data_frame, columns=("POSITION", "TRANS", "TRANS_WAIT", "SANS", "SANS_WAIT", "PERIOD", "SAMPLE_IDENTIFICATION_STRING", "BACKGROUND", "THICKNESS", "TEMPERATURE", "TEMPERATURE2", "FIELD", "SHEAR_RATE_1", "SHEAR_RATE_2", "SHEAR_ANGLE_1", "SHEAR_ANGLE_2", "PRE_COMMAND", "POST_COMMAND", "RB_NUMBER"), show="headings") + self.data_grid.grid(row=0, column=0, sticky="nsew") + scrollbar_y = ttk.Scrollbar(self.data_frame, orient=tk.VERTICAL, command=self.data_grid.yview) + scrollbar_y.grid(row=0, column=1, sticky="ns") + self.data_grid.configure(yscrollcommand=scrollbar_y.set) + + scrollbar_x = ttk.Scrollbar(self.data_frame, orient=tk.HORIZONTAL, command=self.data_grid.xview) + scrollbar_x.grid(row=1, column=0, sticky="ew") + self.data_grid.configure(xscrollcommand=scrollbar_x.set) + + for col in self.data_grid["columns"]: - self.data_grid.heading(col, text=col.replace("__", " ")) + self.data_grid.heading(col, text=col.replace("_", " ")) # Settings Panel self.pnl_settings = SettingsPanel(self, self.settings) self.pnl_settings.grid(row=0, column=0, sticky="ew") @@ -72,10 +89,7 @@ def create_widgets(self): self.btn_open_excel = ttk.Button(self, text="Open Excel", command=self.btn_open_excel_click) self.btn_open_excel.grid(row=3, column=0, padx=5, pady=5, sticky="w") - - - - + self.mnu_main = tk.Menu(self.parent) self.parent.config(menu=self.mnu_main) @@ -93,13 +107,10 @@ def create_widgets(self): instrument_menu.add_radiobutton(label="LOQ", variable=self.instrument_var, value="LOQ") instrument_menu.add_radiobutton(label="SANS2D", variable=self.instrument_var, value="SANS2D") instrument_menu.add_radiobutton(label="ZOOM", variable=self.instrument_var, value="ZOOM") - - - - - - - + ''' + def onFrameConfigure(self, event): + self.canvas.configure(scrollregion=self.canvas.bbox("all")) + ''' def load_last_instrument(self): inst = "LARMOR" # Replace with actual settings retrieval self.instrument_var.set(inst) @@ -115,6 +126,7 @@ def pnl_settings_on_write_clicked(self): def pnl_settings_on_preview_clicked(self): script = self.inst_info.script_generator.generate_script(self.data.runs, self.settings.experiment, "my_script") + print(script) preview_dialog = PreviewDialog(self, script) preview_dialog.show() diff --git a/Models/InstrumentInfo.py b/Models/InstrumentInfo.py index 72598aa..a0928d9 100644 --- a/Models/InstrumentInfo.py +++ b/Models/InstrumentInfo.py @@ -75,7 +75,7 @@ def script_generator(self): @script_generator.setter def script_generator(self, value): self._script_generator = value - +''' class ScriptGenerator: @staticmethod def generate_script(runs, experiment, scriptname): @@ -84,7 +84,7 @@ def generate_script(runs, experiment, scriptname): @staticmethod def estimate_run_time(runs, experiment, estimate): return len(runs) * 10 # Dummy implementation - +''' # Example usage if __name__ == "__main__": def on_property_changed(name): diff --git a/Properties/Settings.ini b/Properties/Settings.ini index 8e8260a..37ce0f0 100644 --- a/Properties/Settings.ini +++ b/Properties/Settings.ini @@ -7,7 +7,7 @@ runorder = 'Alternate - TRANS first' numtrans = '1' numsans = '1' loopperrun = 'False' -a2settingsans = -a2settingtrans = +a2settingsans = +a2settingtrans = collectionmode = 'Events' diff --git a/SANS_Script_GUI_v4.csproj.py b/SANS_Script_GUI_v4.csproj.py index 10d6092..f312438 100644 --- a/SANS_Script_GUI_v4.csproj.py +++ b/SANS_Script_GUI_v4.csproj.py @@ -89,12 +89,11 @@ "Models\\A2Setting.py", "Models\\Estimation.py", "Models\\Geometry.py", - "Models\\RunOrder.py", "EstimationPanel.xaml.py", "Models\\Experiment.py", "Models\\ExperimentSettings.py", "Models\\WaitForUnits.py", - "Scripting\\GeniePythonScriptGenerator.py", + "Scripting\\ScriptGenerator.py", "Scripting\\IScriptGenerator.py", "Scripting\\OpenGenieScriptGenerator.py", "SettingsPanel.xaml.py", diff --git a/Scripting/IScriptGenerator.py b/Scripting/IScriptGenerator.py deleted file mode 100644 index 4b8aca1..0000000 --- a/Scripting/IScriptGenerator.py +++ /dev/null @@ -1,10 +0,0 @@ -from abc import ABC, abstractmethod - -class IScriptGenerator(ABC): - @abstractmethod - def generate_script(self, runs, settings, scriptname): - pass - - @abstractmethod - def estimate_run_time(self, runs, settings, estimation): - pass diff --git a/Scripting/OpenGenieScriptGenerator.py b/Scripting/OpenGenieScriptGenerator.py deleted file mode 100644 index f8329ef..0000000 --- a/Scripting/OpenGenieScriptGenerator.py +++ /dev/null @@ -1,309 +0,0 @@ -from datetime import datetime, timedelta -from Models.Estimation import Estimation -from Models.Experiment import Experiment -from Models.ExperimentSettings import ExperimentSettings - -class OpenGenieScriptGenerator: - def __init__(self, is_sans2d): - self.is_sans2d = is_sans2d - - def generate_script(self, runs, settings, scriptname): - script = f"# Script created by LOQ Script at {datetime.now()}\n" - script += "# Script MUST be loaded in to Open GENIE using the loadscript command.\n\n" - - if self.is_sans2d: - script += "QUALIFIERS /test\n" - - script += f"LOCAL transCount = 0 sansCount = 0 numTRANS={settings.num_trans} numSANS={settings.num_sans}\n\n" - script += "SETSCRIPTNAME THIS_PROCEDURE()\n\n" - script += f'Sample_par width={settings.sample_width} height={settings.sample_height} geometry="{settings.sample_geometry}"\n\n' - - if self.is_sans2d: - position_list = ",".join([exp.position for exp in runs if exp.position]) - if position_list: - script += f'IF NOT CHECK_MOVE_POS("{position_list}")\n' - script += ' PRINTEN "Invalid positions - script aborted"\n' - script += ' RETURN\n' - script += 'ENDIF\n\n' - script += "IF test\n" - script += " RETURN\n" - script += "ENDIF\n\n" - - if settings.order == "AllTrans": - if settings.loop_per_run: - script += self.do_all_trans_loop_each(runs, settings, True, True) - script += self.do_all_sans_loop_each(runs, settings, True, True) - else: - script += "LOOP\n\n" - script += self.do_all_trans(runs, settings, True, True) - script += self.do_all_sans(runs, settings, True, True) - script += "EXITIF ((transCount >= numTRANS) AND (sansCount >= numSANS))\n\n" - script += "ENDLOOP\n\n" - elif settings.order == "TransFirst": - if settings.loop_per_run: - for exp in runs: - script += self.do_single_trans_loop_each(exp, settings, True, False) - script += self.do_single_sans_loop_each(exp, settings, False, True) - else: - script += "LOOP\n\n" - for exp in runs: - script += self.do_single_trans(exp, settings, True, False) - script += self.do_single_sans(exp, settings, False, True) - script += "transCount = transCount + 1\n" - script += "sansCount = sansCount + 1\n\n" - script += "EXITIF ((transCount >= numTRANS) AND (sansCount >= numSANS))\n\n" - script += "ENDLOOP\n\n" - elif settings.order == "AllSans": - if settings.loop_per_run: - script += self.do_all_sans_loop_each(runs, settings, True, True) - script += self.do_all_trans_loop_each(runs, settings, True, True) - else: - script += "LOOP\n\n" - script += self.do_all_sans(runs, settings, True, True) - script += self.do_all_trans(runs, settings, True, True) - script += "EXITIF ((transCount >= numTRANS) AND (sansCount >= numSANS))\n\n" - script += "ENDLOOP\n\n" - elif settings.order == "SansFirst": - if settings.loop_per_run: - for exp in runs: - script += self.do_single_sans_loop_each(exp, settings, True, False) - script += self.do_single_trans_loop_each(exp, settings, False, True) - else: - script += "LOOP\n\n" - for exp in runs: - script += self.do_single_sans(exp, settings, True, False) - script += self.do_single_trans(exp, settings, False, True) - script += "transCount = transCount + 1\n" - script += "sansCount = sansCount + 1\n\n" - script += "EXITIF ((transCount >= numTRANS) AND (sansCount >= numSANS))\n\n" - script += "ENDLOOP\n\n" - - script += 'PRINTN "SCRIPT COMPLETED "\n\n' - return script - - def do_single_sans(self, exp, settings, do_pre_com, do_post_com): - script = "" - if settings.num_sans > 0 and exp.sans > 0: - script += "IF (sansCount < numSANS)\n" - script += self.set_sans_aperture(settings.a2_setting_sans) - script += self.do_single_run(False, exp, False, do_pre_com, do_post_com, settings) - script += "\nENDIF\n\n" - return script - - def do_single_trans(self, exp, settings, do_pre_com, do_post_com): - script = "" - if settings.num_trans > 0 and exp.trans > 0: - script += "IF (transCount < numTRANS)\n" - script += self.set_trans_aperture(settings.a2_setting_trans) - script += self.do_single_run(True, exp, False, do_pre_com, do_post_com, settings) - script += "\nENDIF\n\n" - return script - - def do_single_sans_loop_each(self, exp, settings, do_pre_com, do_post_com): - script = "" - if settings.num_sans > 0 and exp.sans > 0: - script += self.set_sans_aperture(settings.a2_setting_sans) - script += self.do_single_run(False, exp, True, do_pre_com, do_post_com, settings) + "\n" - return script - - def do_single_trans_loop_each(self, exp, settings, do_pre_com, do_post_com): - script = "" - if settings.num_trans > 0 and exp.trans > 0: - script += self.set_trans_aperture(settings.a2_setting_trans) - script += self.do_single_run(True, exp, True, do_pre_com, do_post_com, settings) + "\n" - return script - - def do_all_sans(self, runs, settings, do_pre_com, do_post_com): - script = "" - if settings.num_sans > 0: - script += self.set_sans_aperture(settings.a2_setting_sans) - script += "IF sansCount < numSANS\n\n" - for exp in runs: - script += self.do_single_run(False, exp, False, do_pre_com, do_post_com, settings) + "\n" - script += "sansCount = sansCount + 1\n\n" - script += "ENDIF\n\n" - return script - - def do_all_trans(self, runs, settings, do_pre_com, do_post_com): - script = "" - if settings.num_trans > 0: - script += self.set_trans_aperture(settings.a2_setting_trans) - script += "IF transCount < numTRANS\n\n" - for exp in runs: - script += self.do_single_run(True, exp, False, do_pre_com, do_post_com, settings) + "\n" - script += "transCount = transCount + 1\n\n" - script += "ENDIF\n\n" - return script - - def do_all_sans_loop_each(self, runs, settings, do_pre_com, do_post_com): - script = "" - if settings.num_sans > 0: - script += self.set_sans_aperture(settings.a2_setting_sans) - for exp in runs: - script += self.do_single_run(False, exp, True, do_pre_com, do_post_com, settings) + "\n" - return script - - def do_all_trans_loop_each(self, runs, settings, do_pre_com, do_post_com): - script = "" - if settings.num_trans > 0: - script += self.set_trans_aperture(settings.a2_setting_trans) - for exp in runs: - script += self.do_single_run(True, exp, True, do_pre_com, do_post_com, settings) + "\n" - return script - - def set_sans_aperture(self, a2_size_sans): - script = "" - if a2_size_sans == "Large": - script += "DO_SANS /LARGE\n\n" - elif a2_size_sans == "Medium": - script += "DO_SANS /MEDIUM\n\n" - elif a2_size_sans == "Small": - script += "DO_SANS /SMALL\n\n" - elif a2_size_sans == "Trans": - script += "DO_SANS /TRANS\n\n" - return script - - def set_trans_aperture(self, a2_size_trans): - script = "" - if not a2_size_trans: - script += "DO_TRANS\n\n" - else: - if a2_size_trans == "Large": - script += "DO_TRANS /LARGE\n\n" - elif a2_size_trans == "Medium": - script += "DO_TRANS /MEDIUM\n\n" - elif a2_size_trans == "Small": - script += "DO_TRANS /SMALL\n\n" - elif a2_size_trans == "Trans": - script += "DO_TRANS /TRANS\n\n" - return script - - def do_single_run(self, is_trans, exp, is_loop, do_pre_com, do_post_com, settings): - script = "" - if exp.trans > 0 or exp.sans > 0: - if exp.rb_number: - script += f"change rbno={exp.rb_number}\n" - if exp.position: - if do_pre_com: - script += self.do_sample_environment(exp) - if is_loop: - if is_trans: - script += "LOOP transCount FROM 1 TO numTRANS\n" - else: - script += "LOOP sansCount FROM 1 TO numSANS\n" - script += self.create_move_command(is_trans, exp, settings) - script += "ENDLOOP\n" - else: - script += self.create_move_command(is_trans, exp, settings) - if do_post_com and exp.post_command: - script += exp.post_command + "\n" - return script - - def do_sample_environment(self, exp): - script = "" - if exp.temperature1: - script += f"CSET TEMP={exp.temperature1}\n" - if exp.temperature2: - script += f"CSET TEMP2={exp.temperature2}\n" - if exp.pre_command: - script += exp.pre_command + "\n" - if exp.field: - script += f"CSET FIELD={exp.field}\n" - if exp.shear_rate1: - script += f"CSET SHEAR_RATE_1={exp.shear_rate1}\n" - if exp.shear_rate2: - script += f"CSET SHEAR_RATE_2={exp.shear_rate2}\n" - if exp.shear_angle1: - script += f"CSET SHEAR_ANGLE_1={exp.shear_angle1}\n" - if exp.shear_angle2: - script += f"CSET SHEAR_ANGLE_2={exp.shear_angle2}\n" - return script - - def create_move_command(self, is_trans, exp, settings): - script = f'MOVE pos="{exp.position}"' - fulltitle = f"{{{exp.sample}}}" - if exp.background: - fulltitle += f"_{{{exp.background}}}" - if exp.thickness: - script += f" thick={exp.thickness}" - else: - script += " thick=1" - if exp.period: - script += f" period={exp.period}" - if is_trans: - script += self.write_wait_for(exp.trans_wait, exp.trans) - if fulltitle: - script += f' title="{fulltitle}_TRANS"' - else: - script += self.write_wait_for(exp.sans_wait, exp.sans) - if fulltitle: - if settings.collection_mode in ["TRANSSANS_Events", "TRANSSANS_Histogram"]: - script += f' title="{fulltitle}_TRANS_SANS"' - else: - script += f' title="{fulltitle}_SANS"' - return script + "\n" - - def write_wait_for(self, waitfor, length): - script = "" - if waitfor: - if waitfor.lower() == "MicroAmps".lower(): - script += f" uAhr={length}" - elif waitfor.lower() == "Minutes".lower(): - script += f" min={length}" - elif waitfor.lower() == "Seconds".lower(): - script += f" seconds={length}" - elif waitfor.lower() == "Frames".lower(): - script += f" frame={length}" - else: - script += f" uAhr={length}" - else: - script += f" uAhr={length}" - return script - - def estimate_run_time(self, runs, settings, estimation): - minutes = 0 - if settings.num_sans > 0: - minutes += self.calculate_time(runs, True, estimation.count_rate, estimation.move_time) * settings.num_sans - if settings.num_trans > 0: - minutes += self.calculate_time(runs, False, estimation.count_rate, estimation.move_time) * settings.num_trans - time = datetime(1, 1, 1, 0, 0, 0) + timedelta(minutes=minutes) - return time.strftime("%H:%M:%S") - - @staticmethod - def calculate_time(runs, is_sans, count_rate, seconds_between): - minutes = 0 - for exp in runs: - if exp.position: - if is_sans: - minutes += OpenGenieScriptGenerator.get_minutes(exp.sans_wait, exp.sans, count_rate) - else: - minutes += OpenGenieScriptGenerator.get_minutes(exp.trans_wait, exp.trans, count_rate) - minutes += seconds_between / 60 - return minutes - - @staticmethod - def get_minutes(wait, length, count_rate): - if wait == "Minutes": - return length - elif wait == "Seconds": - return length / 60.0 - elif wait == "Frames": - return length / (25.0 * 60.0) - else: - return length / (count_rate / 60.0) - - - - - - - -if __name__ == "__main__": - runs = [Experiment(position="pos1", trans=10, sans=20, trans_wait="Minutes", sans_wait="Minutes", sample="Sample1", thickness=1.0)] - settings = ExperimentSettings(sample_width="10", sample_height="10", sample_geometry="Flat", order="AllTrans", num_trans=1, num_sans=1, collection_mode="Events", a2_setting_trans="Large", a2_setting_sans="Large", loop_per_run=False) - estimation = Estimation(count_rate=100, move_time=10) - generator = OpenGenieScriptGenerator(is_sans2d=True) - script = generator.generate_script(runs, settings, "my_script") - print(script) - estimated_time = generator.estimate_run_time(runs, settings, estimation) - print(f"Estimated run time: {estimated_time}") \ No newline at end of file diff --git a/Scripting/GeniePythonScriptGenerator.py b/Scripting/ScriptGenerator.py similarity index 97% rename from Scripting/GeniePythonScriptGenerator.py rename to Scripting/ScriptGenerator.py index 51ca3fe..751cb0a 100644 --- a/Scripting/GeniePythonScriptGenerator.py +++ b/Scripting/ScriptGenerator.py @@ -3,7 +3,7 @@ from Models.Experiment import Experiment from Models.ExperimentSettings import ExperimentSettings -class GeniePythonScriptGenerator: +class ScriptGenerator: def __init__(self, is_larmor, is_loq, is_zoom, is_sans2d): self.indent = " " self.is_larmor = is_larmor @@ -215,9 +215,9 @@ def calculate_time(runs, is_sans, count_rate, seconds_between): for exp in runs: if exp.position: if is_sans: - minutes += GeniePythonScriptGenerator.get_minutes(exp.sans_wait, exp.sans, count_rate) + minutes += ScriptGenerator.get_minutes(exp.sans_wait, exp.sans, count_rate) else: - minutes += GeniePythonScriptGenerator.get_minutes(exp.trans_wait, exp.trans, count_rate) + minutes += ScriptGenerator.get_minutes(exp.trans_wait, exp.trans, count_rate) minutes += seconds_between / 60 return minutes @@ -249,7 +249,7 @@ class A2Setting: runs = [Experiment(position="pos1", trans=10, sans=20, trans_wait="Minutes", sans_wait="Minutes", sample="Sample1", thickness=1.0)] settings = ExperimentSettings(sample_width="10", sample_height="10", sample_geometry="Flat", order="AllTrans", num_trans=1, num_sans=1, collection_mode="Events", a2_setting_trans="Large", a2_setting_sans="Large") estimation = Estimation(count_rate=100, move_time=10) - generator = GeniePythonScriptGenerator(is_larmor=True, is_loq=False, is_zoom=False, is_sans2d=False) + generator = ScriptGenerator(is_larmor=True, is_loq=False, is_zoom=False, is_sans2d=False) script = generator.generate_script(runs, settings, "my_script") print(script) estimated_time = generator.estimate_run_time(runs, settings, estimation) diff --git a/SettingsPanel.py b/SettingsPanel.py index 76b1f91..c08cbf4 100644 --- a/SettingsPanel.py +++ b/SettingsPanel.py @@ -29,6 +29,7 @@ def create_widgets(self): # Order ttk.Label(grid_frame, text="Order:").grid(row=0, column=0, sticky=tk.E) + print(self.settings_vm.order_choices) self.com_order = ttk.Combobox(grid_frame, values=self.settings_vm.order_choices) self.com_order.grid(row=0, column=1, columnspan=2, padx=5, pady=5) diff --git a/ViewModels/DataCollection.py b/ViewModels/DataCollection.py deleted file mode 100644 index 40ce416..0000000 --- a/ViewModels/DataCollection.py +++ /dev/null @@ -1,4 +0,0 @@ -class DataCollection: - @staticmethod - def choices(): - return ["Mode1", "Mode2", "Mode3"] \ No newline at end of file diff --git a/ViewModels/Geometry.py b/ViewModels/Geometry.py deleted file mode 100644 index bdc6546..0000000 --- a/ViewModels/Geometry.py +++ /dev/null @@ -1,4 +0,0 @@ -class Geometry: - @staticmethod - def choices(): - return ["Geometry1", "Geometry2", "Geometry3"] \ No newline at end of file diff --git a/ViewModels/InstrumentVM.py b/ViewModels/InstrumentVM.py index 49fb38a..b74bd98 100644 --- a/ViewModels/InstrumentVM.py +++ b/ViewModels/InstrumentVM.py @@ -4,7 +4,7 @@ from Models.InstrumentInfo import InstrumentInfo from Models.Estimation import Estimation from Models.ExperimentSettings import ExperimentSettings -from Scripting.GeniePythonScriptGenerator import GeniePythonScriptGenerator +from Scripting.ScriptGenerator import ScriptGenerator from Properties.Settings import Settings @@ -56,7 +56,7 @@ def set_instrument(self, larmor, loq, sans2d, zoom, a2_label, a2_choices, count_ self.is_sans2d.set(sans2d) self.is_zoom.set(zoom) - InstrumentInfo.script_generator = GeniePythonScriptGenerator(larmor, loq, zoom, sans2d) + InstrumentInfo.script_generator = ScriptGenerator(larmor, loq, zoom, sans2d) InstrumentInfo.a2_label = a2_label InstrumentInfo.a2_choices = a2_choices diff --git a/ViewModels/RunOrder.py b/ViewModels/RunOrder.py deleted file mode 100644 index bec23ec..0000000 --- a/ViewModels/RunOrder.py +++ /dev/null @@ -1,4 +0,0 @@ -class RunOrder: - @staticmethod - def choices(): - return ["Choice1", "Choice2", "Choice3"] \ No newline at end of file diff --git a/ViewModels/SettingsPanelVM.py b/ViewModels/SettingsPanelVM.py index a5a5a2a..7d38c93 100644 --- a/ViewModels/SettingsPanelVM.py +++ b/ViewModels/SettingsPanelVM.py @@ -11,8 +11,9 @@ class SettingsPanelVM: def __init__(self, inst_info=None): self.property_changed = [] - self.order_choices = ["Choice1", "Choice2", "Choice3"] - self.geometry_choices = ["Geometry1", "Geometry2", "Geometry3"] + self.order_choices = RunOrder.get_choices() + self.geometry_choices = Geometry.get_choices() + self.collection_mode_choices = DataCollection.get_choices() self._experiment = ExperimentSettings() @@ -39,18 +40,19 @@ def inst_info(self): def inst_info(self, value): self._inst_info = value self.on_property_changed("inst_info") - + ''' @staticmethod def order_choices(): - return RunOrder.choices() + return RunOrder.get_choices() @staticmethod def geometry_choices(): - return Geometry.choices() - + return Geometry.get_choices() + @staticmethod def collection_mode_choices(): return DataCollection.choices() + ''' From f817fff56e83fb1699bd624b77a88a95f8994f96 Mon Sep 17 00:00:00 2001 From: Dirk Honecker Date: Mon, 23 Dec 2024 21:16:02 +0000 Subject: [PATCH 05/88] Creating Table with tkintertable --- TableWidget.py | 216 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 216 insertions(+) create mode 100644 TableWidget.py diff --git a/TableWidget.py b/TableWidget.py new file mode 100644 index 0000000..c9c4628 --- /dev/null +++ b/TableWidget.py @@ -0,0 +1,216 @@ + +from tkintertable import * #TableCanvas, TableModel, ColumnHeader, RowHeader + +from tkinter import * +import random +from collections import OrderedDict + +data = {'rec1': {'col1': 99.88, 'col2': 108.79, 'label': 'rec1','col3': 99.88, 'col4': 108.79, 'label2': 'rec1','col5': 99.88, 'col6': 108.79, 'label3': 'rec1'}, + 'rec2': {'col1': 99.88, 'col2': 108.79, 'label': 'rec1','col3': 99.88, 'col4': 108.79, 'label2': 'rec1','col5': 99.88, 'col6': 108.79, 'label3': 'rec1'}, + 'rec3': {'col1': 99.88, 'col2': 108.79, 'label': 'rec1','col3': 99.88, 'col4': 108.79, 'label2': 'rec1','col5': 99.88, 'col6': 108.79, 'label3': 'rec1'}, + 'rec4': {'col1': 99.88, 'col2': 108.79, 'label': 'rec1','col3': 99.88, 'col4': 108.79, 'label2': 'rec1','col5': 99.88, 'col6': 108.79, 'label3': 'rec1'}, + 'rec5': {'col1': 99.88, 'col2': 108.79, 'label': 'rec1','col3': 99.88, 'col4': 108.79, 'label2': 'rec1','col5': 99.88, 'col6': 108.79, 'label3': 'rec1'}, + 'rec6': {'col1': 99.88, 'col2': 108.79, 'label': 'rec1','col3': 99.88, 'col4': 108.79, 'label2': 'rec1','col5': 99.88, 'col6': 108.79, 'label3': 'rec1'}, + 'rec7': {'col1': 99.88, 'col2': 108.79, 'label': 'rec1','col3': 99.88, 'col4': 108.79, 'label2': 'rec1','col5': 99.88, 'col6': 108.79, 'label3': 'rec1'}, + 'rec8': {'col1': 99.88, 'col2': 108.79, 'label': 'rec1','col3': 99.88, 'col4': 108.79, 'label2': 'rec1','col5': 99.88, 'col6': 108.79, 'label3': 'rec1'}, + 'rec9': {'col1': 99.88, 'col2': 108.79, 'label': 'rec1','col3': 99.88, 'col4': 108.79, 'label2': 'rec1','col5': 99.88, 'col6': 108.79, 'label3': 'rec1'} + } + +class CustomRowHeader(RowHeader): + def customCommand(self): + """Define your custom command here""" + print("Custom command executed") + + +class CustomColumnHeader(ColumnHeader): + def popupMenu(self, event): + """Customize the popup menu for the column header""" + + colname = self.model.columnNames[self.table.currentcol] + collabel = self.model.columnlabels[colname] + currcol = self.table.currentcol + popupmenu = Menu(self, tearoff=0) + + def popupFocusOut(event): + popupmenu.unpost() + + # Default actions from the original class + popupmenu.add_command(label="Sort by " + collabel, command=lambda: self.table.sortTable(currcol)) + popupmenu.add_command(label="Sort by " + collabel + ' (descending)', command=lambda: self.table.sortTable(currcol, reverse=1)) + #popupmenu.add_command(label="Delete This Column", command=self.table.deleteColumn) + + # Adding a custom command + #popupmenu.add_command(label="Custom Command", command=self.customCommand) + + popupmenu.bind("", popupFocusOut) + popupmenu.focus_set() + popupmenu.post(event.x_root, event.y_root) + return popupmenu + + def customCommand(self): + """Define your custom command here""" + print("Custom command executed") + + +class CustomTableCanvas(TableCanvas): + + def show(self, callback=None): + """Adds column header and scrollbars and combines them with + the current table adding all to the master frame provided in constructor. + Table is then redrawn.""" + + #Add the table and header to the frame + self.tablerowheader = CustomRowHeader(self.parentframe, self, width=self.rowheaderwidth) + self.tablecolheader = CustomColumnHeader(self.parentframe, self) + self.Yscrollbar = AutoScrollbar(self.parentframe,orient=VERTICAL,command=self.set_yviews) + self.Yscrollbar.grid(row=1,column=2,rowspan=1,sticky='news',pady=0,ipady=0) + self.Xscrollbar = AutoScrollbar(self.parentframe,orient=HORIZONTAL,command=self.set_xviews) + self.Xscrollbar.grid(row=2,column=1,columnspan=1,sticky='news') + self['xscrollcommand'] = self.Xscrollbar.set + self['yscrollcommand'] = self.Yscrollbar.set + self.tablecolheader['xscrollcommand'] = self.Xscrollbar.set + self.tablerowheader['yscrollcommand'] = self.Yscrollbar.set + self.parentframe.rowconfigure(1,weight=1) + self.parentframe.columnconfigure(1,weight=1) + + self.tablecolheader.grid(row=0,column=1,rowspan=1,sticky='news',pady=0,ipady=0) + self.tablerowheader.grid(row=1,column=0,rowspan=1,sticky='news',pady=0,ipady=0) + self.grid(row=1,column=1,rowspan=1,sticky='news',pady=0,ipady=0) + + self.adjustColumnWidths() + self.redrawTable(callback=callback) + self.parentframe.bind("", self.redrawVisible) + self.tablecolheader.xview("moveto", 0) + self.xview("moveto", 0) + return + + def updateModel(self, model): + """Call this method to update the table model""" + + self.model = model + self.rows = self.model.getRowCount() + self.cols = self.model.getColumnCount() + self.tablewidth = (self.cellwidth)*self.cols + self.tablecolheader = CustomColumnHeader(self.parentframe, self) + self.tablerowheader = CustomRowHeader(self.parentframe, self) + self.createTableFrame() + return + + def popupMenu(self, event, rows=None, cols=None, outside=None): + """Add left and right click behaviour for canvas, should not have to override + this function, it will take its values from defined dicts in constructor""" + + # Default actions from the original class + defaultactions = { + "Set Fill Color": lambda: self.setcellColor(rows, cols, key='bg'), + "Set Text Color": lambda: self.setcellColor(rows, cols, key='fg'), + "Copy": lambda: self.copyCell(rows, cols), + "Paste": lambda: self.pasteCell(rows, cols), + "Fill Down": lambda: self.fillDown(rows, cols), + "Fill Right": lambda: self.fillAcross(cols, rows), + "Add Row(s)": lambda: self.addRows(), + "Delete Row(s)": lambda: self.deleteRow(), + "Clear Data": lambda: self.deleteCells(rows, cols), + "Select All": self.select_All, + "New": self.new, + "Load": self.load, + "Save": self.save, + "Import text": self.importTable, + "Export csv": self.exportTable, + "Export Table": self.exportTable, + "Preferences": self.showtablePrefs, + } + + # Adding a custom command + #defaultactions["Custom Command"] = self.customCommand + + main = ["Set Fill Color", "Set Text Color", "Copy", "Paste", "Fill Down", "Fill Right", "Clear Data"] + general = ["Select All", "Add Row(s)", "Delete Row(s)", "Preferences"] + filecommands = ['New', 'Load', 'Save', 'Import text', 'Export csv'] + + def createSubMenu(parent, label, commands): + menu = Menu(parent, tearoff=0) + popupmenu.add_cascade(label=label, menu=menu) + for action in commands: + menu.add_command(label=action, command=defaultactions[action]) + return menu + + def add_commands(fieldtype): + """Add commands to popup menu for column type and specific cell""" + functions = self.columnactions[fieldtype] + for f in functions.keys(): + func = getattr(self, functions[f]) + popupmenu.add_command(label=f, command=lambda: func(row, col)) + return + + popupmenu = Menu(self, tearoff=0) + def popupFocusOut(event): + popupmenu.unpost() + + if outside == None: + # if outside table, just show general items + row = self.get_row_clicked(event) + col = self.get_col_clicked(event) + coltype = self.model.getColumnType(col) + def add_defaultcommands(): + """now add general actions for all cells""" + for action in main: + if action == 'Fill Down' and (rows == None or len(rows) <= 1): + continue + if action == 'Fill Right' and (cols == None or len(cols) <= 1): + continue + else: + popupmenu.add_command(label=action, command=defaultactions[action]) + return + + if coltype in self.columnactions: + add_commands(coltype) + add_defaultcommands() + + for action in general: + popupmenu.add_command(label=action, command=defaultactions[action]) + + popupmenu.add_separator() + popupmenu.bind("", popupFocusOut) + popupmenu.focus_set() + popupmenu.post(event.x_root, event.y_root) + return popupmenu + +# def customCommand(self): +# """Define your custom command here""" +# print("Custom command executed") + +class TableWidget(Frame): + """Basic test frame for the table""" + + def __init__(self, parent=None): + self.parent = parent + Frame.__init__(self) + self.main = self.master + self.main.geometry('800x500+200+100') + self.main.title('Test') + f = Frame(self.main) + f.pack(fill=BOTH,expand=1) + self.table = CustomTableCanvas(f, data=data) + #table.importCSV('test.csv') + print (self.table.model.columnNames) + #table.model.data[1]['a'] = 'XX' + #table.model.setValueAt('YY',0,2) + + self.table.show() + + + + def customOption(self): + print("Custom option selected") + return + + + +if __name__ == "__main__": + + app = TableWidget() + app.mainloop() + + + From 94eb9b2b43c79a7fe9b9cd975573e8c9192e0e2d Mon Sep 17 00:00:00 2001 From: Dirk Honecker Date: Mon, 23 Dec 2024 23:40:48 +0000 Subject: [PATCH 06/88] Import data from excel file into table widget --- TableWidget.py | 103 ++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 88 insertions(+), 15 deletions(-) diff --git a/TableWidget.py b/TableWidget.py index c9c4628..7798562 100644 --- a/TableWidget.py +++ b/TableWidget.py @@ -2,10 +2,19 @@ from tkintertable import * #TableCanvas, TableModel, ColumnHeader, RowHeader from tkinter import * +from openpyxl import load_workbook import random from collections import OrderedDict -data = {'rec1': {'col1': 99.88, 'col2': 108.79, 'label': 'rec1','col3': 99.88, 'col4': 108.79, 'label2': 'rec1','col5': 99.88, 'col6': 108.79, 'label3': 'rec1'}, +empty_data = { + 'rec1': { + 'col1': '', 'col2': '', 'label': '', 'col3': '', 'col4': '', 'label2': '', 'col5': '', 'col6': '', 'label3': '' + } +} + +data=None +''' +data={'rec1': {'col1': 99.88, 'col2': 108.79, 'label': 'rec1','col3': 99.88, 'col4': 108.79, 'label2': 'rec1','col5': 99.88, 'col6': 108.79, 'label3': 'rec1'}, 'rec2': {'col1': 99.88, 'col2': 108.79, 'label': 'rec1','col3': 99.88, 'col4': 108.79, 'label2': 'rec1','col5': 99.88, 'col6': 108.79, 'label3': 'rec1'}, 'rec3': {'col1': 99.88, 'col2': 108.79, 'label': 'rec1','col3': 99.88, 'col4': 108.79, 'label2': 'rec1','col5': 99.88, 'col6': 108.79, 'label3': 'rec1'}, 'rec4': {'col1': 99.88, 'col2': 108.79, 'label': 'rec1','col3': 99.88, 'col4': 108.79, 'label2': 'rec1','col5': 99.88, 'col6': 108.79, 'label3': 'rec1'}, @@ -15,6 +24,13 @@ 'rec8': {'col1': 99.88, 'col2': 108.79, 'label': 'rec1','col3': 99.88, 'col4': 108.79, 'label2': 'rec1','col5': 99.88, 'col6': 108.79, 'label3': 'rec1'}, 'rec9': {'col1': 99.88, 'col2': 108.79, 'label': 'rec1','col3': 99.88, 'col4': 108.79, 'label2': 'rec1','col5': 99.88, 'col6': 108.79, 'label3': 'rec1'} } +''' + + + + + + class CustomRowHeader(RowHeader): def customCommand(self): @@ -96,37 +112,90 @@ def updateModel(self, model): self.createTableFrame() return + def deleteCells(self, rows=None, cols=None): + """Clear the cell contents""" + + n = messagebox.askyesno("Clear Confirm", + "Clear this data?", + parent=self.parentframe) + if not n: + return + for col in cols: + for row in rows: + #absrow = self.get_AbsoluteRow(row) + self.model.deleteCellRecord(row, col) + self.redrawCell(row,col) + return + + def clearData(self, evt=None): + """Delete cells from gui event""" + + rows = self.multiplerowlist + cols = self.multiplecollist + self.deleteCells(rows, cols) + return + + def fillDown(self, rowlist, collist): + """Fill down a column, or multiple columns""" + model = self.model + #absrow = self.get_AbsoluteRow(rowlist[0]) + #remove first element as we don't want to overwrite it + #rowlist.remove(rowlist[0]) + return + + def load_excel_file(self): + global workbook + filepath = filedialog.askopenfilename(filetypes=[("Excel Files", "*.xlsx")]) + if filepath: + self.workbook = load_workbook(filepath) + sheet_names = self.workbook.sheetnames + self.display_sheet_data(sheet_names[0]) + + def display_sheet_data(self,name=None): + sheet_name = name + if sheet_name and self.workbook: + sheet = self.workbook[sheet_name] + + data={} + for i, row in enumerate(sheet.iter_rows(values_only=True), start=1): + data[f'rec{i}'] = { + 'col1': row[0], 'col2': row[1], 'label': row[2], 'col3': row[3], 'col4': row[4], 'label2': row[5], 'col5': row[6], 'col6': row[7], 'label3': row[8] + } + self.data = data + self.model.importDict(data) + self.redraw() + + def importTable(self): + self.load_excel_file() + def popupMenu(self, event, rows=None, cols=None, outside=None): """Add left and right click behaviour for canvas, should not have to override this function, it will take its values from defined dicts in constructor""" # Default actions from the original class defaultactions = { - "Set Fill Color": lambda: self.setcellColor(rows, cols, key='bg'), - "Set Text Color": lambda: self.setcellColor(rows, cols, key='fg'), + #"Set Fill Color": lambda: self.setcellColor(rows, cols, key='bg'), + #"Set Text Color": lambda: self.setcellColor(rows, cols, key='fg'), "Copy": lambda: self.copyCell(rows, cols), "Paste": lambda: self.pasteCell(rows, cols), "Fill Down": lambda: self.fillDown(rows, cols), - "Fill Right": lambda: self.fillAcross(cols, rows), "Add Row(s)": lambda: self.addRows(), "Delete Row(s)": lambda: self.deleteRow(), - "Clear Data": lambda: self.deleteCells(rows, cols), + "Clear Data": lambda: self.clearData(), "Select All": self.select_All, "New": self.new, - "Load": self.load, - "Save": self.save, - "Import text": self.importTable, - "Export csv": self.exportTable, + "Import Table": self.importTable, "Export Table": self.exportTable, + "Export Reductionbatch": self.exportTable, "Preferences": self.showtablePrefs, } # Adding a custom command #defaultactions["Custom Command"] = self.customCommand - main = ["Set Fill Color", "Set Text Color", "Copy", "Paste", "Fill Down", "Fill Right", "Clear Data"] - general = ["Select All", "Add Row(s)", "Delete Row(s)", "Preferences"] - filecommands = ['New', 'Load', 'Save', 'Import text', 'Export csv'] + main = [ "Copy", "Paste", "Fill Down", "Clear Data"] + general = ["Select All", "Add Row(s)", "Delete Row(s)", "Preferences",'New', 'Import Table', 'Export Table', 'Export Reductionbatch'] + def createSubMenu(parent, label, commands): menu = Menu(parent, tearoff=0) @@ -157,8 +226,6 @@ def add_defaultcommands(): for action in main: if action == 'Fill Down' and (rows == None or len(rows) <= 1): continue - if action == 'Fill Right' and (cols == None or len(cols) <= 1): - continue else: popupmenu.add_command(label=action, command=defaultactions[action]) return @@ -184,6 +251,7 @@ class TableWidget(Frame): """Basic test frame for the table""" def __init__(self, parent=None): + self.data=None self.parent = parent Frame.__init__(self) self.main = self.master @@ -191,7 +259,9 @@ def __init__(self, parent=None): self.main.title('Test') f = Frame(self.main) f.pack(fill=BOTH,expand=1) - self.table = CustomTableCanvas(f, data=data) + if self.data == None: + self.data = empty_data + self.table = CustomTableCanvas(f, data=self.data) #table.importCSV('test.csv') print (self.table.model.columnNames) #table.model.data[1]['a'] = 'XX' @@ -207,6 +277,9 @@ def customOption(self): + + + if __name__ == "__main__": app = TableWidget() From f1db7744151d8b9564ecaa0d0ef0dcde134d7924 Mon Sep 17 00:00:00 2001 From: Dirk Honecker Date: Tue, 24 Dec 2024 12:59:16 +0000 Subject: [PATCH 07/88] create new row when pressing enter --- TableWidget.py | 55 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/TableWidget.py b/TableWidget.py index 7798562..8a938f5 100644 --- a/TableWidget.py +++ b/TableWidget.py @@ -168,6 +168,61 @@ def display_sheet_data(self,name=None): def importTable(self): self.load_excel_file() + def do_bindings(self): + """Bind keys and mouse clicks, this can be overriden""" + self.bind("",self.handle_left_click) + self.bind("",self.handle_double_click) + self.bind("", self.handle_left_ctrl_click) + self.bind("", self.handle_left_shift_click) + + self.bind("", self.handle_left_release) + if self.ostyp=='mac': + #For mac we bind Shift, left-click to right click + self.bind("", self.handle_right_click) + self.bind('',self.handle_right_click) + else: + self.bind("", self.handle_right_click) + + self.bind('', self.handle_mouse_drag) + self.bind('', self.handle_motion) + + self.bind_all("", self.deleteRow) + self.bind_all("", self.addRow) + self.bind_all("", self.clearData) + self.bind_all("", self.paste) + + #if not hasattr(self,'parentapp'): + # self.parentapp = self.parentframe + + self.parentframe.master.bind_all("", self.handle_arrow_keys) + self.parentframe.master.bind_all("", self.handle_arrow_keys) + self.parentframe.master.bind_all("", self.handle_arrow_keys) + self.parentframe.master.bind_all("", self.handle_arrow_keys) + self.parentframe.master.bind_all("", self.handle_arrow_keys) + #self.parentframe.master.bind_all("", self.handle_arrow_keys) + self.parentframe.master.bind_all("", self.handle_arrow_keys) + self.parentframe.master.bind_all("", self.move_to_next_row) + + #if 'windows' in self.platform: + self.bind("", self.mouse_wheel) + self.bind('', self.mouse_wheel) + self.bind('', self.mouse_wheel) + self.focus_set() + return + + + def move_to_next_row(self, event): + """Move to the next row when enter is pressed and create a new row if it doesn't exist""" + current_row = self.getSelectedRow() + if current_row >= self.rows - 1: + self.addRow() + self.setSelectedRow(current_row + 1) + self.drawSelectedRect(self.currentrow, self.currentcol) + self.drawSelectedRow() + coltype = self.model.getColumnType(self.currentcol) + if coltype == 'text' or coltype == 'number': + self.drawCellEntry(self.currentrow, self.currentcol) + def popupMenu(self, event, rows=None, cols=None, outside=None): """Add left and right click behaviour for canvas, should not have to override this function, it will take its values from defined dicts in constructor""" From 8bd70013913f876668bfe907eb8efb0aed6d6ecf Mon Sep 17 00:00:00 2001 From: Dirk Honecker Date: Thu, 26 Dec 2024 07:35:03 +0000 Subject: [PATCH 08/88] Initiate table entry list, and constructor for data_list --- App.config.py | 4 +++- TableDataConstructor.py | 46 +++++++++++++++++++++++++++++++++++++++++ TableWidget.py | 39 +++++++++++++++++++--------------- app_config.ini | 1 + 4 files changed, 72 insertions(+), 18 deletions(-) create mode 100644 TableDataConstructor.py diff --git a/App.config.py b/App.config.py index 31cf442..84450c0 100644 --- a/App.config.py +++ b/App.config.py @@ -13,7 +13,9 @@ 'LoopPerRun': 'False', 'A2SettingSans': 'Large = 25, 16 mm', 'A2SettingTrans': 'Large = 25, 16 mm', - 'CollectionMode': 'Events' + 'CollectionMode': 'Events', + 'TableEntries': 'POSITION, TRANS, TRANS_WAIT, SANS, SANS_WAIT, PERIOD, SAMPLE_IDENTIFICATION_STRING, BACKGROUND, THICKNESS, TEMPERATURE, TEMPERATURE2, FIELD, SHEAR_RATE_1, SHEAR_RATE_2, SHEAR_ANGLE_1, SHEAR_ANGLE_2, PRE_COMMAND, POST_COMMAND, RB_NUMBER' + } with open('app_config.ini', 'w') as configfile: diff --git a/TableDataConstructor.py b/TableDataConstructor.py new file mode 100644 index 0000000..313982a --- /dev/null +++ b/TableDataConstructor.py @@ -0,0 +1,46 @@ +class TableDataConstructor: + def __init__(self, table_entries): + self.table_entries = table_entries.split(', ') + + def construct_empty_data(self): + empty_data = { + 'rec1': {entry: '' for entry in self.table_entries} + } + return empty_data + + def construct_data(self, data_list): + data = {} + for i, row in enumerate(data_list, start=1): + data[f'rec{i}'] = {entry: value for entry, value in zip(self.table_entries, row)} + return data + + def deconstruct_data(self, data_dict): + data_list = [] + for key in sorted(data_dict.keys(), key=lambda x: int(x[3:])): + row = [data_dict[key][entry] for entry in self.table_entries] + data_list.append(row) + return data_list + +# Example usage +TableEntries = 'POSITION, TRANS, TRANS_WAIT, SANS, SANS_WAIT, PERIOD, SAMPLE_IDENTIFICATION_STRING, BACKGROUND, THICKNESS, TEMPERATURE, TEMPERATURE2, FIELD, SHEAR_RATE_1, SHEAR_RATE_2, SHEAR_ANGLE_1, SHEAR_ANGLE_2, PRE_COMMAND, POST_COMMAND, RB_NUMBER' +constructor = TableDataConstructor(TableEntries) +empty_data = constructor.construct_empty_data() +print(empty_data) + +# Example usage +TableEntries = 'col1, col2, label, col3, col4, label2, col5, col6, label3' +constructor = TableDataConstructor(TableEntries) + +# Construct data from a list of lists +data_list = [ + [99.88, 108.79, 'rec1', 99.88, 108.79, 'rec1', 99.88, 108.79, 'rec1'], + [99.88, 108.79, 'rec2', 99.88, 108.79, 'rec2', 99.88, 108.79, 'rec2'], + [99.88, 108.79, 'rec3', 99.88, 108.79, 'rec3', 99.88, 108.79, 'rec3']] + # Add more rows as needed + +data = constructor.construct_data(data_list) +print("Data:", data) + +# Deconstruct data back to list of lists +deconstructed_data_list = constructor.deconstruct_data(data) +print("Deconstructed Data List:", deconstructed_data_list) diff --git a/TableWidget.py b/TableWidget.py index 8a938f5..327c9ee 100644 --- a/TableWidget.py +++ b/TableWidget.py @@ -12,8 +12,7 @@ } } -data=None -''' + data={'rec1': {'col1': 99.88, 'col2': 108.79, 'label': 'rec1','col3': 99.88, 'col4': 108.79, 'label2': 'rec1','col5': 99.88, 'col6': 108.79, 'label3': 'rec1'}, 'rec2': {'col1': 99.88, 'col2': 108.79, 'label': 'rec1','col3': 99.88, 'col4': 108.79, 'label2': 'rec1','col5': 99.88, 'col6': 108.79, 'label3': 'rec1'}, 'rec3': {'col1': 99.88, 'col2': 108.79, 'label': 'rec1','col3': 99.88, 'col4': 108.79, 'label2': 'rec1','col5': 99.88, 'col6': 108.79, 'label3': 'rec1'}, @@ -24,7 +23,7 @@ 'rec8': {'col1': 99.88, 'col2': 108.79, 'label': 'rec1','col3': 99.88, 'col4': 108.79, 'label2': 'rec1','col5': 99.88, 'col6': 108.79, 'label3': 'rec1'}, 'rec9': {'col1': 99.88, 'col2': 108.79, 'label': 'rec1','col3': 99.88, 'col4': 108.79, 'label2': 'rec1','col5': 99.88, 'col6': 108.79, 'label3': 'rec1'} } -''' + @@ -303,20 +302,22 @@ def add_defaultcommands(): # print("Custom command executed") class TableWidget(Frame): - """Basic test frame for the table""" + """Frame for the table""" + EMPTY_DATA = { + 'rec1': { + 'col1': '', 'col2': '', 'label': '', 'col3': '', 'col4': '', 'label2': '', 'col5': '', 'col6': '', 'label3': '' + } + } - def __init__(self, parent=None): - self.data=None + def __init__(self, parent=None, data_set=None): + Frame.__init__(self, parent) + print(data_set) + print(TableWidget.EMPTY_DATA) self.parent = parent - Frame.__init__(self) - self.main = self.master - self.main.geometry('800x500+200+100') - self.main.title('Test') - f = Frame(self.main) - f.pack(fill=BOTH,expand=1) - if self.data == None: - self.data = empty_data - self.table = CustomTableCanvas(f, data=self.data) + if data_set is None: + data_set = TableWidget.EMPTY_DATA + self.data = data_set + self.table = CustomTableCanvas(self, data=self.data) #table.importCSV('test.csv') print (self.table.model.columnNames) #table.model.data[1]['a'] = 'XX' @@ -337,8 +338,12 @@ def customOption(self): if __name__ == "__main__": - app = TableWidget() - app.mainloop() + root = Tk() + root.geometry('800x500+200+100') + root.title('Test') + app = TableWidget(root) + app.pack(fill=BOTH, expand=1) + root.mainloop() diff --git a/app_config.ini b/app_config.ini index 1e228bb..a400303 100644 --- a/app_config.ini +++ b/app_config.ini @@ -10,4 +10,5 @@ LoopPerRun = 'False' A2SettingSans = 'Large = 25, 16 mm' A2SettingTrans = 'Large = 25, 16 mm' CollectionMode = 'Events' +TableEntries = 'POSITION, TRANS, TRANS_WAIT, SANS, SANS_WAIT, PERIOD, SAMPLE_IDENTIFICATION_STRING, BACKGROUND, THICKNESS, TEMPERATURE, TEMPERATURE2, FIELD, SHEAR_RATE_1, SHEAR_RATE_2, SHEAR_ANGLE_1, SHEAR_ANGLE_2, PRE_COMMAND, POST_COMMAND, RB_NUMBER' From e384f1d313df8dfc30fc034f689da551990faa59 Mon Sep 17 00:00:00 2001 From: Dirk Honecker Date: Thu, 26 Dec 2024 13:16:10 +0000 Subject: [PATCH 09/88] import csv --- TableWidget.py | 82 +++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 67 insertions(+), 15 deletions(-) diff --git a/TableWidget.py b/TableWidget.py index 327c9ee..46bf936 100644 --- a/TableWidget.py +++ b/TableWidget.py @@ -5,14 +5,16 @@ from openpyxl import load_workbook import random from collections import OrderedDict +from TableDataConstructor import TableDataConstructor empty_data = { 'rec1': { 'col1': '', 'col2': '', 'label': '', 'col3': '', 'col4': '', 'label2': '', 'col5': '', 'col6': '', 'label3': '' } } - - +''' +data=None +''' data={'rec1': {'col1': 99.88, 'col2': 108.79, 'label': 'rec1','col3': 99.88, 'col4': 108.79, 'label2': 'rec1','col5': 99.88, 'col6': 108.79, 'label3': 'rec1'}, 'rec2': {'col1': 99.88, 'col2': 108.79, 'label': 'rec1','col3': 99.88, 'col4': 108.79, 'label2': 'rec1','col5': 99.88, 'col6': 108.79, 'label3': 'rec1'}, 'rec3': {'col1': 99.88, 'col2': 108.79, 'label': 'rec1','col3': 99.88, 'col4': 108.79, 'label2': 'rec1','col5': 99.88, 'col6': 108.79, 'label3': 'rec1'}, @@ -66,6 +68,48 @@ def customCommand(self): """Define your custom command here""" print("Custom command executed") +class TableImporter: + """Provides import utility methods for the Table and Table Model classes""" + + def __init__(self): + """Setup globals""" + self.separator_list = [',', ' ', '\t', ':'] + self.var_sep = StringVar() + self.var_sep.set(',') + return + + @staticmethod + def ImportTableData(table, sep=None): + """Import table data from a comma separated file and populate the table model""" + + parent = table.parentframe + filename = filedialog.askopenfilename(parent=parent, defaultextension='.csv', + filetypes=[("CSV files", "*.csv")]) + if not filename: + return + + if sep is None: + sep = ',' + + if not os.path.isfile(filename): + return None + + dictdata = {} + try: + with open(filename, "r") as csvfile: + reader = csv.DictReader(csvfile, delimiter=sep) + for count, rec in enumerate(reader): + dictdata[count] = rec + except Exception as e: + messagebox.showerror("Import Error", f"Failed to import data: {str(e)}") + return None + + model = table.getModel() + model.deleteRows() + model.importDict(dictdata) + table.redrawTable() + return dictdata + class CustomTableCanvas(TableCanvas): @@ -154,18 +198,24 @@ def display_sheet_data(self,name=None): sheet_name = name if sheet_name and self.workbook: sheet = self.workbook[sheet_name] - - data={} - for i, row in enumerate(sheet.iter_rows(values_only=True), start=1): - data[f'rec{i}'] = { - 'col1': row[0], 'col2': row[1], 'label': row[2], 'col3': row[3], 'col4': row[4], 'label2': row[5], 'col5': row[6], 'col6': row[7], 'label3': row[8] - } + TableEntries = 'col1, col2, label, col3, col4, label2, col5, col6, label3' + constructor = TableDataConstructor(TableEntries) + data = constructor.construct_data(sheet.iter_rows(values_only=True)) self.data = data self.model.importDict(data) self.redraw() def importTable(self): - self.load_excel_file() + #self.load_excel_file() + self.importCSV() + + + def importCSV(self, filename=None, sep=','): + """Import from csv file""" + + importer = TableImporter() + importer.ImportTableData(self) + return def do_bindings(self): """Bind keys and mouse clicks, this can be overriden""" @@ -309,14 +359,14 @@ class TableWidget(Frame): } } - def __init__(self, parent=None, data_set=None): + def __init__(self, parent=None, data=None): Frame.__init__(self, parent) - print(data_set) + print(data) print(TableWidget.EMPTY_DATA) self.parent = parent - if data_set is None: - data_set = TableWidget.EMPTY_DATA - self.data = data_set + if data is None: + data = TableWidget.EMPTY_DATA + self.data = data self.table = CustomTableCanvas(self, data=self.data) #table.importCSV('test.csv') print (self.table.model.columnNames) @@ -341,7 +391,9 @@ def customOption(self): root = Tk() root.geometry('800x500+200+100') root.title('Test') - app = TableWidget(root) + if data==None: + data = TableWidget.EMPTY_DATA + app = TableWidget(root, data=data) app.pack(fill=BOTH, expand=1) root.mainloop() From d5f823091925f303b9ec7f2b8ac7c08be1ddb29f Mon Sep 17 00:00:00 2001 From: Dirk Honecker Date: Thu, 26 Dec 2024 21:59:55 +0000 Subject: [PATCH 10/88] show default values in gui --- App.config.py | 5 ++-- EstimationPanel.py | 4 +++ MainWindow.py | 49 ++++++++++++-------------------- Models/A2Setting.py | 1 - Models/ExperimentSettings.py | 53 ++++++++++++++++++----------------- Models/RunOrder.py | 4 +-- Properties/Settings.ini | 21 ++++++++------ Properties/Settings.py | 18 ++++++++++++ Scripting/ScriptGenerator.py | 20 ++++--------- SettingsPanel.py | 24 ++++++++++++++-- TableWidget.py | 41 +++++++-------------------- ViewModels/A2Setting.py | 6 ---- ViewModels/SettingsPanelVM.py | 4 +-- app_config.ini | 17 +++++------ 14 files changed, 133 insertions(+), 134 deletions(-) delete mode 100644 ViewModels/A2Setting.py diff --git a/App.config.py b/App.config.py index 84450c0..a8c5a2b 100644 --- a/App.config.py +++ b/App.config.py @@ -14,8 +14,9 @@ 'A2SettingSans': 'Large = 25, 16 mm', 'A2SettingTrans': 'Large = 25, 16 mm', 'CollectionMode': 'Events', - 'TableEntries': 'POSITION, TRANS, TRANS_WAIT, SANS, SANS_WAIT, PERIOD, SAMPLE_IDENTIFICATION_STRING, BACKGROUND, THICKNESS, TEMPERATURE, TEMPERATURE2, FIELD, SHEAR_RATE_1, SHEAR_RATE_2, SHEAR_ANGLE_1, SHEAR_ANGLE_2, PRE_COMMAND, POST_COMMAND, RB_NUMBER' - + 'TableEntries': 'POSITION, TRANS, TRANS_WAIT, SANS, SANS_WAIT, PERIOD, SAMPLE_IDENTIFICATION_STRING, BACKGROUND, THICKNESS, TEMPERATURE, TEMPERATURE2, FIELD, SHEAR_RATE_1, SHEAR_RATE_2, SHEAR_ANGLE_1, SHEAR_ANGLE_2, PRE_COMMAND, POST_COMMAND, RB_NUMBER', + 'count_rate': '38', + 'move_time': '120' } with open('app_config.ini', 'w') as configfile: diff --git a/EstimationPanel.py b/EstimationPanel.py index 6372d99..064087e 100644 --- a/EstimationPanel.py +++ b/EstimationPanel.py @@ -2,6 +2,7 @@ from tkinter import ttk from ViewModels.EstimationPanelVM import EstimationPanelVM from Models.Estimation import Estimation +from Properties.Settings import Settings class EstimationPanel(ttk.Frame): def __init__(self, parent, estimate = None): @@ -11,6 +12,7 @@ def __init__(self, parent, estimate = None): self.create_widgets() def create_widgets(self): + settings = Settings() #self.pack(fill=tk.BOTH, expand=True) self.grid(sticky="nsew") @@ -26,6 +28,7 @@ def create_widgets(self): count_rate_frame = ttk.Frame(grid_frame) count_rate_frame.grid(row=0, column=1, columnspan=2) self.txt_count_rate = ttk.Entry(count_rate_frame, width=10, textvariable=self.estimate.count_rate) + self.txt_count_rate.insert(0, settings.count_rate) # Default value self.txt_count_rate.grid(row=0, column=0, padx=5, pady=5) ttk.Label(count_rate_frame, text="µA/hr").grid(row=0, column=1) @@ -34,6 +37,7 @@ def create_widgets(self): move_time_frame = ttk.Frame(grid_frame) move_time_frame.grid(row=1, column=1, columnspan=2) self.txt_move_time = ttk.Entry(move_time_frame, width=10, textvariable=self.estimate.move_time) + self.txt_move_time.insert(0, settings.move_time) # Default value self.txt_move_time.grid(row=0, column=0, padx=5, pady=5) ttk.Label(move_time_frame, text="seconds").grid(row=0, column=1) diff --git a/MainWindow.py b/MainWindow.py index bde4c06..1188ad0 100644 --- a/MainWindow.py +++ b/MainWindow.py @@ -13,7 +13,11 @@ from Dialogs.PreviewDialog import PreviewDialog from SettingsPanel import SettingsPanel from EstimationPanel import EstimationPanel +from TableWidget import TableWidget +from Properties.Settings import Settings +TableEntries = 'POSITION, TRANS, TRANS_WAIT, SANS, SANS_WAIT, PERIOD, SAMPLE_IDENTIFICATION_STRING, BACKGROUND, THICKNESS, TEMPERATURE, TEMPERATURE2, FIELD, SHEAR_RATE_1, SHEAR_RATE_2, SHEAR_ANGLE_1, SHEAR_ANGLE_2, PRE_COMMAND, POST_COMMAND, RB_NUMBER' + class MainWindow(tk.Frame): def __init__(self,parent): @@ -31,47 +35,25 @@ def __init__(self,parent): self.create_widgets() self.load_last_instrument() + def create_widgets(self): #self.data_grid = ttk.Treeview(self) self.grid_columnconfigure(1, weight=1) self.grid_rowconfigure(1, weight=1) - - - - # Data Grid with horizontal and vertical scrollbars + + self.data_frame = TableWidget(self) + #self.data_frame.grid(row=0, column=1, rowspan=4, sticky="nsew", padx=5, pady=5) + self.data_frame.grid(row=0, column=1, sticky="nsew", padx=5, pady=5) ''' - self.canvas = tk.Canvas(self, borderwidth=0, background="#ffffff") - self.data_frame = tk.Frame(self.canvas, background="#f0ffff") - #data_frame = ttk.Frame(self) - self.scrolly = tk.Scrollbar(self, orient="vertical", command=self.canvas.yview) - self.scrollx = tk.Scrollbar(self, orient="horizontal", command=self.canvas.xview) - self.canvas.configure(xscrollcommand=self.scrollx.set) - self.canvas.configure(yscrollcommand=self.scrolly.set) - self.canvas.create_window((4,4), window=self.data_frame, anchor="nw", tags="self.frame") - - self.data_frame.bind("", self.onFrameConfigure) - ''' - - self.data_frame = ttk.Frame(self) - self.data_frame.grid(row=0, column=1, rowspan=4, sticky="nsew", padx=5, pady=5) - self.data_grid = ttk.Treeview(self.data_frame, columns=("POSITION", "TRANS", "TRANS_WAIT", "SANS", "SANS_WAIT", "PERIOD", "SAMPLE_IDENTIFICATION_STRING", "BACKGROUND", "THICKNESS", "TEMPERATURE", "TEMPERATURE2", "FIELD", "SHEAR_RATE_1", "SHEAR_RATE_2", "SHEAR_ANGLE_1", "SHEAR_ANGLE_2", "PRE_COMMAND", "POST_COMMAND", "RB_NUMBER"), show="headings") - self.data_grid.grid(row=0, column=0, sticky="nsew") - scrollbar_y = ttk.Scrollbar(self.data_frame, orient=tk.VERTICAL, command=self.data_grid.yview) - scrollbar_y.grid(row=0, column=1, sticky="ns") - self.data_grid.configure(yscrollcommand=scrollbar_y.set) - - scrollbar_x = ttk.Scrollbar(self.data_frame, orient=tk.HORIZONTAL, command=self.data_grid.xview) - scrollbar_x.grid(row=1, column=0, sticky="ew") - self.data_grid.configure(xscrollcommand=scrollbar_x.set) - - for col in self.data_grid["columns"]: self.data_grid.heading(col, text=col.replace("_", " ")) + ''' + # Settings Panel self.pnl_settings = SettingsPanel(self, self.settings) self.pnl_settings.grid(row=0, column=0, sticky="ew") @@ -112,9 +94,12 @@ def onFrameConfigure(self, event): self.canvas.configure(scrollregion=self.canvas.bbox("all")) ''' def load_last_instrument(self): - inst = "LARMOR" # Replace with actual settings retrieval + settings = Settings() + inst = settings.last_instrument # Replace with actual settings retrieval self.instrument_var.set(inst) + + def pnl_settings_on_write_clicked(self): filetypes = [("genie_python Files", "*.py")] if self.instrument_var.get() in ["LARMOR", "LOQ", "ZOOM", "SANS2D"] else [("Open Genie Files", "*.gcl")] filepath = filedialog.asksaveasfilename(defaultextension=filetypes[0][1], filetypes=filetypes) @@ -175,6 +160,8 @@ def on_close(self): else: last_instrument = "LARMOR" # Save last_instrument to settings + settings = Settings() + settings.last_instrument(last_instrument) self.destroy() @@ -188,5 +175,5 @@ def on_close(self): app.grid(sticky="nsew") root.grid_rowconfigure(0, weight=1) root.grid_columnconfigure(0, weight=1) - app.protocol("WM_DELETE_WINDOW", app.on_close) + #app.protocol("WM_DELETE_WINDOW", app.on_close) app.mainloop() \ No newline at end of file diff --git a/Models/A2Setting.py b/Models/A2Setting.py index 7a55dee..51591d5 100644 --- a/Models/A2Setting.py +++ b/Models/A2Setting.py @@ -4,7 +4,6 @@ class A2Setting: MEDIUM = "Medium = 12 mm" SMALL = "Small = 5/1 mm" TRANS = "Trans = 1 mm" - LARMOR = "" LARMOR_LARGE = "Large = 25, 16 mm" LARMOR_MEDIUM = "Medium = 20, 14 mm" LARMOR_SMALL = "Small = 15, 10 mm" diff --git a/Models/ExperimentSettings.py b/Models/ExperimentSettings.py index c448c54..1a65435 100644 --- a/Models/ExperimentSettings.py +++ b/Models/ExperimentSettings.py @@ -1,23 +1,22 @@ import tkinter as tk -from configparser import ConfigParser +from Properties.Settings import Settings class ExperimentSettings: - def __init__(self, config_file='settings.ini'): - self.config = ConfigParser() - self.config.read(config_file) - self.config_file = config_file - - self._order = tk.StringVar(value=self.config['DEFAULT'].get('RunOrder', 'TransFirst')) - self._num_sans = tk.IntVar(value=self.config['DEFAULT'].getint('NumSans', 1)) - self._num_trans = tk.IntVar(value=self.config['DEFAULT'].getint('NumTrans', 1)) - self._loop_per_run = tk.BooleanVar(value=self.config['DEFAULT'].getboolean('LoopPerRun', False)) - self._a2_setting_sans = tk.StringVar(value=self.config['DEFAULT'].get('A2SettingSans', 'Large')) - self._a2_setting_trans = tk.StringVar(value=self.config['DEFAULT'].get('A2SettingTrans', 'Large')) - self._sample_geometry = tk.StringVar(value=self.config['DEFAULT'].get('Geometry', 'Flat Plate')) - self._sample_height = tk.DoubleVar(value=self.config['DEFAULT'].getfloat('SampleHeight', 11)) - self._sample_width = tk.DoubleVar(value=self.config['DEFAULT'].getfloat('SampleWidth', 11)) + def __init__(self): + settings = Settings() + + self._order = tk.StringVar(value=settings.run_order) + self._num_sans = tk.IntVar(value=settings.num_sans) + self._num_trans = tk.IntVar(value=settings.num_trans) + self._loop_per_run = tk.BooleanVar(value=settings.loop_per_run) + self._a2_setting_sans = tk.StringVar(value=settings.a2_setting_sans) + self._a2_setting_trans = tk.StringVar(value=settings.a2_setting_trans) + self._sample_geometry = tk.StringVar(value=settings.geometry) + self._sample_height = tk.DoubleVar(value=settings.sample_height) + self._sample_width = tk.DoubleVar(value=settings.sample_width) self._sample_width_enabled = tk.BooleanVar(value=True) - self._collection_mode = tk.StringVar(value=self.config['DEFAULT'].get('CollectionMode', 'Events')) + + self._collection_mode = tk.StringVar(value=settings.collection_mode) self._is_valid = tk.BooleanVar(value=True) self.property_changed = [] @@ -38,16 +37,18 @@ def on_property_changed(self, *args): self.save() def save(self): - self.config['DEFAULT']['RunOrder'] = self._order.get() - self.config['DEFAULT']['NumSans'] = str(self._num_sans.get()) - self.config['DEFAULT']['NumTrans'] = str(self._num_trans.get()) - self.config['DEFAULT']['LoopPerRun'] = str(self._loop_per_run.get()) - self.config['DEFAULT']['A2SettingSans'] = self._a2_setting_sans.get() - self.config['DEFAULT']['A2SettingTrans'] = self._a2_setting_trans.get() - self.config['DEFAULT']['Geometry'] = self._sample_geometry.get() - self.config['DEFAULT']['SampleHeight'] = str(self._sample_height.get()) - self.config['DEFAULT']['SampleWidth'] = str(self._sample_width.get()) - self.config['DEFAULT']['CollectionMode'] = self._collection_mode.get() + settings = Settings() + settings.run_order(str(self._order.get())) + settings.num_sans(str(self._num_sans.get())) + settings.num_sans(str(self._num_trans.get())) + settings.loop_per_run(str(self._loop_per_run.get())) + print(self._a2_setting_sans.get()) + settings.a2_setting_sans(self._a2_setting_sans.get()) + settings.a2_setting_trans(self._a2_setting_trans.get()) + settings.geometry(self._sample_geometry.get()) + settings.sample_height(str(self._sample_height.get())) + settings.sample_width(str(self._sample_width.get())) + settings.collection_mode(self._collection_mode.get()) with open(self.config_file, 'w') as configfile: self.config.write(configfile) diff --git a/Models/RunOrder.py b/Models/RunOrder.py index 73e5815..ea95720 100644 --- a/Models/RunOrder.py +++ b/Models/RunOrder.py @@ -4,12 +4,12 @@ ALL_SANS = "All SANS first" SANS_FIRST = "Alternate - SANS first" - choices = [ + choices = ( ALL_TRANS, TRANS_FIRST, ALL_SANS, SANS_FIRST - ] + ) @staticmethod def get_choices(): diff --git a/Properties/Settings.ini b/Properties/Settings.ini index 37ce0f0..9fff4e1 100644 --- a/Properties/Settings.ini +++ b/Properties/Settings.ini @@ -1,13 +1,16 @@ [DEFAULT] lastinstrument = LOQ -sampleheight = '11' -samplewidth = '11' -geometry = 'Flat Plate' -runorder = 'Alternate - TRANS first' -numtrans = '1' -numsans = '1' -loopperrun = 'False' -a2settingsans = -a2settingtrans = +sampleheight = 11 +samplewidth = 11 +geometry = Flat Plate +runorder = Alternate - TRANS first +numtrans = 1 +numsans = 1 +loopperrun = False +a2settingsans = +a2settingtrans = collectionmode = 'Events' +tableentries = 'POSITION, TRANS, TRANS_WAIT, SANS, SANS_WAIT, PERIOD, SAMPLE_IDENTIFICATION_STRING, BACKGROUND, THICKNESS, TEMPERATURE, TEMPERATURE2, FIELD, SHEAR_RATE_1, SHEAR_RATE_2, SHEAR_ANGLE_1, SHEAR_ANGLE_2, PRE_COMMAND, POST_COMMAND, RB_NUMBER' +count_rate = 38 +move_time = 120 diff --git a/Properties/Settings.py b/Properties/Settings.py index 6d9f60f..0b9b36f 100644 --- a/Properties/Settings.py +++ b/Properties/Settings.py @@ -108,6 +108,24 @@ def collection_mode(self, value): self.dict_settings.set('DEFAULT','CollectionMode', str(value)) self.save() + @property + def count_rate(self): + return self.dict_settings.get('DEFAULT','count_rate') + + @count_rate.setter + def count_rate(self, value): + self.dict_settings.set('DEFAULT','count_rate', str(value)) + self.save() + + @property + def move_time(self): + return self.dict_settings.get('DEFAULT','move_time') + + @move_time.setter + def move_time(self, value): + self.dict_settings.set('DEFAULT','move_time', str(value)) + self.save() + def save(self): with open(self.setting_ini_path, 'w') as configfile: self.dict_settings.write(configfile) diff --git a/Scripting/ScriptGenerator.py b/Scripting/ScriptGenerator.py index 751cb0a..fcee1a5 100644 --- a/Scripting/ScriptGenerator.py +++ b/Scripting/ScriptGenerator.py @@ -2,6 +2,7 @@ from Models.Estimation import Estimation from Models.Experiment import Experiment from Models.ExperimentSettings import ExperimentSettings +from Models.A2Setting import A2Setting class ScriptGenerator: def __init__(self, is_larmor, is_loq, is_zoom, is_sans2d): @@ -154,13 +155,14 @@ def do_sans_run(self, script, exp, indent, settings): return script def aperture_string(self, size): - if size in [A2Setting.Large, A2Setting.Larmor]: + a2setting = A2Setting() + if size in [a2setting.Large, a2setting.Larmor]: return "large" - elif size in [A2Setting.Medium, A2Setting.Larmor]: + elif size in [a2setting.Medium, a2setting.Larmor]: return "medium" - elif size in [A2Setting.Small, A2Setting.Larmor]: + elif size in [a2setting.Small, a2setting.Larmor]: return "small" - elif size in [A2Setting.XLarge, A2Setting.Larmor]: + elif size in [a2setting.XLarge, a2setting.Larmor]: return "xlarge" return "" @@ -232,16 +234,6 @@ def get_minutes(wait, length, count_rate): else: return length / (count_rate / 60.0) -class A2Setting: - Large = "Large" - Medium = "Medium" - Small = "Small" - XLarge = "XLarge" - Larmor = "Larmor" - - - - diff --git a/SettingsPanel.py b/SettingsPanel.py index c08cbf4..2199f0d 100644 --- a/SettingsPanel.py +++ b/SettingsPanel.py @@ -1,7 +1,7 @@ import tkinter as tk from tkinter import ttk from ViewModels.SettingsPanelVM import SettingsPanelVM - +from Properties.Settings import Settings class SettingsPanel(ttk.Frame): def __init__(self, parent, settings_vm=None): @@ -19,7 +19,7 @@ def create_widgets(self): #self.write_button = ttk.Button(self, text="Write Script", command=self.on_write_clicked) #self.write_button.pack(side=tk.LEFT, padx=5, pady=5) - + settings = Settings() label = ttk.Label(self, text="Settings", font=("Helvetica", 16, "bold")) label.grid(row=0, column=0, columnspan=2, pady=10) @@ -30,14 +30,30 @@ def create_widgets(self): # Order ttk.Label(grid_frame, text="Order:").grid(row=0, column=0, sticky=tk.E) print(self.settings_vm.order_choices) + print(settings.run_order) + self.com_order = ttk.Combobox(grid_frame, values=self.settings_vm.order_choices) + self.com_order['values']=self.settings_vm.order_choices self.com_order.grid(row=0, column=1, columnspan=2, padx=5, pady=5) + self.com_order.set(settings.run_order) # Set default value + ''' + # Collection Mode + ttk.Label(grid_frame, text="Collection Mode:").grid(row=0, column=0, sticky=tk.E) + print(self.settings_vm.collection_mode_choices) + print(settings.collection_mode) + + self.collect_mode = ttk.Combobox(grid_frame, values=self.settings_vm.collection_mode_choices) + self.collect_mode['values']=self.settings_vm.collection_mode_choices + self.collect_mode.grid(row=1, column=1, columnspan=2, padx=5, pady=5) + self.collect_mode.set(settings.collection_mode) # Set default value + ''' # Do SANS ttk.Label(grid_frame, text="Do SANS:").grid(row=1, column=0, sticky=tk.E) sans_frame = ttk.Frame(grid_frame) sans_frame.grid(row=1, column=1, columnspan=2) self.txt_times_sans = ttk.Entry(sans_frame, width=5) + self.txt_times_sans.insert(0, settings.num_sans) # Default value self.txt_times_sans.grid(row=0, column=0, padx=5, pady=5) ttk.Label(sans_frame, text="time(s)").grid(row=0, column=1) @@ -46,6 +62,7 @@ def create_widgets(self): trans_frame = ttk.Frame(grid_frame) trans_frame.grid(row=2, column=1, columnspan=2) self.txt_times_trans = ttk.Entry(trans_frame, width=5) + self.txt_times_trans.insert(0, settings.num_trans) # Default value self.txt_times_trans.grid(row=0, column=0, padx=5, pady=5) ttk.Label(trans_frame, text="time(s)").grid(row=0, column=1) @@ -57,12 +74,14 @@ def create_widgets(self): ttk.Label(grid_frame, text="Sample Geometry:").grid(row=6, column=0, sticky=tk.E) self.com_sample_geo = ttk.Combobox(grid_frame, values=self.settings_vm.geometry_choices) self.com_sample_geo.grid(row=6, column=1, columnspan=2, padx=5, pady=5) + self.com_sample_geo.set(settings.geometry) # Set default value # Sample Height ttk.Label(grid_frame, text="Sample Height:").grid(row=7, column=0, sticky=tk.E) sample_height_frame = ttk.Frame(grid_frame) sample_height_frame.grid(row=7, column=1, columnspan=2) self.txt_sample_height = ttk.Entry(sample_height_frame, width=5) + self.txt_sample_height.insert(0, settings.sample_height) # Default value self.txt_sample_height.grid(row=0, column=0, padx=5, pady=5) ttk.Label(sample_height_frame, text="mm").grid(row=0, column=1) @@ -71,6 +90,7 @@ def create_widgets(self): sample_width_frame = ttk.Frame(grid_frame) sample_width_frame.grid(row=8, column=1, columnspan=2) self.txt_sample_width = ttk.Entry(sample_width_frame, width=5) + self.txt_sample_width.insert(0, settings.sample_width) # Default value self.txt_sample_width.grid(row=0, column=0, padx=5, pady=5) ttk.Label(sample_width_frame, text="mm").grid(row=0, column=1) diff --git a/TableWidget.py b/TableWidget.py index 46bf936..ce17bcf 100644 --- a/TableWidget.py +++ b/TableWidget.py @@ -7,30 +7,10 @@ from collections import OrderedDict from TableDataConstructor import TableDataConstructor -empty_data = { - 'rec1': { - 'col1': '', 'col2': '', 'label': '', 'col3': '', 'col4': '', 'label2': '', 'col5': '', 'col6': '', 'label3': '' - } -} -''' -data=None -''' -data={'rec1': {'col1': 99.88, 'col2': 108.79, 'label': 'rec1','col3': 99.88, 'col4': 108.79, 'label2': 'rec1','col5': 99.88, 'col6': 108.79, 'label3': 'rec1'}, - 'rec2': {'col1': 99.88, 'col2': 108.79, 'label': 'rec1','col3': 99.88, 'col4': 108.79, 'label2': 'rec1','col5': 99.88, 'col6': 108.79, 'label3': 'rec1'}, - 'rec3': {'col1': 99.88, 'col2': 108.79, 'label': 'rec1','col3': 99.88, 'col4': 108.79, 'label2': 'rec1','col5': 99.88, 'col6': 108.79, 'label3': 'rec1'}, - 'rec4': {'col1': 99.88, 'col2': 108.79, 'label': 'rec1','col3': 99.88, 'col4': 108.79, 'label2': 'rec1','col5': 99.88, 'col6': 108.79, 'label3': 'rec1'}, - 'rec5': {'col1': 99.88, 'col2': 108.79, 'label': 'rec1','col3': 99.88, 'col4': 108.79, 'label2': 'rec1','col5': 99.88, 'col6': 108.79, 'label3': 'rec1'}, - 'rec6': {'col1': 99.88, 'col2': 108.79, 'label': 'rec1','col3': 99.88, 'col4': 108.79, 'label2': 'rec1','col5': 99.88, 'col6': 108.79, 'label3': 'rec1'}, - 'rec7': {'col1': 99.88, 'col2': 108.79, 'label': 'rec1','col3': 99.88, 'col4': 108.79, 'label2': 'rec1','col5': 99.88, 'col6': 108.79, 'label3': 'rec1'}, - 'rec8': {'col1': 99.88, 'col2': 108.79, 'label': 'rec1','col3': 99.88, 'col4': 108.79, 'label2': 'rec1','col5': 99.88, 'col6': 108.79, 'label3': 'rec1'}, - 'rec9': {'col1': 99.88, 'col2': 108.79, 'label': 'rec1','col3': 99.88, 'col4': 108.79, 'label2': 'rec1','col5': 99.88, 'col6': 108.79, 'label3': 'rec1'} - } - - - - - +TableEntries = 'POSITION, TRANS, TRANS_WAIT, SANS, SANS_WAIT, PERIOD, SAMPLE_IDENTIFICATION_STRING, BACKGROUND, THICKNESS, TEMPERATURE, TEMPERATURE2, FIELD, SHEAR_RATE_1, SHEAR_RATE_2, SHEAR_ANGLE_1, SHEAR_ANGLE_2, PRE_COMMAND, POST_COMMAND, RB_NUMBER' + +data=None class CustomRowHeader(RowHeader): @@ -198,7 +178,7 @@ def display_sheet_data(self,name=None): sheet_name = name if sheet_name and self.workbook: sheet = self.workbook[sheet_name] - TableEntries = 'col1, col2, label, col3, col4, label2, col5, col6, label3' + #TableEntries = 'col1, col2, label, col3, col4, label2, col5, col6, label3' constructor = TableDataConstructor(TableEntries) data = constructor.construct_data(sheet.iter_rows(values_only=True)) self.data = data @@ -353,16 +333,15 @@ def add_defaultcommands(): class TableWidget(Frame): """Frame for the table""" - EMPTY_DATA = { - 'rec1': { - 'col1': '', 'col2': '', 'label': '', 'col3': '', 'col4': '', 'label2': '', 'col5': '', 'col6': '', 'label3': '' - } - } + #TableEntries = 'col1, col2, label, col3, col4, label2, col5, col6, label3' + + constructor = TableDataConstructor(TableEntries) + EMPTY_DATA = constructor.construct_empty_data() + def __init__(self, parent=None, data=None): Frame.__init__(self, parent) - print(data) - print(TableWidget.EMPTY_DATA) + self.parent = parent if data is None: data = TableWidget.EMPTY_DATA diff --git a/ViewModels/A2Setting.py b/ViewModels/A2Setting.py deleted file mode 100644 index 773a782..0000000 --- a/ViewModels/A2Setting.py +++ /dev/null @@ -1,6 +0,0 @@ -class A2Setting: - Large = "Large" - Medium = "Medium" - Small = "Small" - XLarge = "XLarge" - Larmor = "Larmor" \ No newline at end of file diff --git a/ViewModels/SettingsPanelVM.py b/ViewModels/SettingsPanelVM.py index 7d38c93..420385f 100644 --- a/ViewModels/SettingsPanelVM.py +++ b/ViewModels/SettingsPanelVM.py @@ -40,7 +40,7 @@ def inst_info(self): def inst_info(self, value): self._inst_info = value self.on_property_changed("inst_info") - ''' + @staticmethod def order_choices(): return RunOrder.get_choices() @@ -52,7 +52,7 @@ def geometry_choices(): @staticmethod def collection_mode_choices(): return DataCollection.choices() - ''' + diff --git a/app_config.ini b/app_config.ini index a400303..38503b7 100644 --- a/app_config.ini +++ b/app_config.ini @@ -1,14 +1,15 @@ [DEFAULT] LastInstrument = 'LARMOR' -SampleHeight = '11' -SampleWidth = '11' -Geometry = 'Flat Plate' -RunOrder = 'Alternate - TRANS first' -NumTrans = '1' -NumSans = '1' -LoopPerRun = 'False' +SampleHeight = 11 +SampleWidth = 11 +Geometry = Flat Plate +RunOrder = Alternate - TRANS first +NumTrans = 1 +NumSans = 1 +LoopPerRun = False A2SettingSans = 'Large = 25, 16 mm' A2SettingTrans = 'Large = 25, 16 mm' CollectionMode = 'Events' TableEntries = 'POSITION, TRANS, TRANS_WAIT, SANS, SANS_WAIT, PERIOD, SAMPLE_IDENTIFICATION_STRING, BACKGROUND, THICKNESS, TEMPERATURE, TEMPERATURE2, FIELD, SHEAR_RATE_1, SHEAR_RATE_2, SHEAR_ANGLE_1, SHEAR_ANGLE_2, PRE_COMMAND, POST_COMMAND, RB_NUMBER' - +Count_Rate=38 +Move_Time=120 From 66f54ea2ff9fcfb77beff7fa706e8b62f2893364 Mon Sep 17 00:00:00 2001 From: Dirk Honecker Date: Fri, 27 Dec 2024 00:06:28 +0000 Subject: [PATCH 11/88] Fix tracking of instrument changes --- MainWindow.py | 19 +++++-------- Models/InstrumentInfo.py | 4 +++ Scripting/ScriptGenerator.py | 26 +++++++++--------- ViewModels/InstrumentVM.py | 52 +++++++++++++----------------------- 4 files changed, 41 insertions(+), 60 deletions(-) diff --git a/MainWindow.py b/MainWindow.py index 1188ad0..e3af20c 100644 --- a/MainWindow.py +++ b/MainWindow.py @@ -15,6 +15,7 @@ from EstimationPanel import EstimationPanel from TableWidget import TableWidget from Properties.Settings import Settings +from Scripting.ScriptGenerator import ScriptGenerator TableEntries = 'POSITION, TRANS, TRANS_WAIT, SANS, SANS_WAIT, PERIOD, SAMPLE_IDENTIFICATION_STRING, BACKGROUND, THICKNESS, TEMPERATURE, TEMPERATURE2, FIELD, SHEAR_RATE_1, SHEAR_RATE_2, SHEAR_ANGLE_1, SHEAR_ANGLE_2, PRE_COMMAND, POST_COMMAND, RB_NUMBER' @@ -105,12 +106,14 @@ def pnl_settings_on_write_clicked(self): filepath = filedialog.asksaveasfilename(defaultextension=filetypes[0][1], filetypes=filetypes) if filepath: scriptname = filepath.split('/')[-1].replace(" ", "_").rsplit('.', 1)[0] - script = self.inst_info.script_generator.generate_script(self.data.runs, self.settings.experiment, scriptname) + script_generator= ScriptGenerator() + script = script_generator.generate_script(self.data.runs, self.settings.experiment, scriptname) with open(filepath, 'w') as file: file.write(script) def pnl_settings_on_preview_clicked(self): - script = self.inst_info.script_generator.generate_script(self.data.runs, self.settings.experiment, "my_script") + script_generator= ScriptGenerator() + script = script_generator.generate_script(self.data.runs, self.settings.experiment, "my_script") print(script) preview_dialog = PreviewDialog(self, script) preview_dialog.show() @@ -149,19 +152,9 @@ def mnu_save_csv_click(self): messagebox.showerror("SANS Script - Error", "Could not save as CSV") def on_close(self): - if self.instrument.is_larmor: - last_instrument = "LARMOR" - elif self.instrument.is_loq: - last_instrument = "LOQ" - elif self.instrument.is_sans2d: - last_instrument = "SANS2D" - elif self.instrument.is_zoom: - last_instrument = "ZOOM" - else: - last_instrument = "LARMOR" # Save last_instrument to settings settings = Settings() - settings.last_instrument(last_instrument) + settings.last_instrument(self.instrument_var) self.destroy() diff --git a/Models/InstrumentInfo.py b/Models/InstrumentInfo.py index a0928d9..53d55ce 100644 --- a/Models/InstrumentInfo.py +++ b/Models/InstrumentInfo.py @@ -2,6 +2,7 @@ from tkinter import StringVar, BooleanVar from typing import List + class InstrumentInfo: property_changed = [] static_property_changed = [] @@ -68,14 +69,17 @@ def collection_mode_enabled(self, value): self._collection_mode_enabled.set(value) self.on_static_property_changed("collection_mode_enabled") +''' @property def script_generator(self): + self._script_generator = GeniePythonScriptGenerator() return self._script_generator @script_generator.setter def script_generator(self, value): self._script_generator = value ''' +''' class ScriptGenerator: @staticmethod def generate_script(runs, experiment, scriptname): diff --git a/Scripting/ScriptGenerator.py b/Scripting/ScriptGenerator.py index fcee1a5..9f7abe9 100644 --- a/Scripting/ScriptGenerator.py +++ b/Scripting/ScriptGenerator.py @@ -5,12 +5,9 @@ from Models.A2Setting import A2Setting class ScriptGenerator: - def __init__(self, is_larmor, is_loq, is_zoom, is_sans2d): + def __init__(self, instrument): self.indent = " " - self.is_larmor = is_larmor - self.is_loq = is_loq - self.is_zoom = is_zoom - self.is_sans2d = is_sans2d + self.is_instrument = instrument def generate_script(self, runs, settings, scriptname): script = f"# Script created by SANSScript at {datetime.now()}\n\n" @@ -28,13 +25,13 @@ def generate_header(self, scriptname): script += "from genie_python import genie as g, BLOCK_NAMES as b\n" script += "sys.path.append(r\"c:\\instrument\\scripts\")\n" - if self.is_larmor: + if self.is_instrument == "LARMOR": script += "from instrument.larmor import * # pylint: disable=wildcard-import, unused-wildcard-import\n" - if self.is_loq: + if self.is_instrument == "LOQ": script += "from instrument.loq import * # pylint: disable=wildcard-import, unused-wildcard-import\n" - if self.is_zoom: + if self.is_instrument == "ZOOM": script += "from instrument.zoom import * # pylint: disable=wildcard-import, unused-wildcard-import\n" - if self.is_sans2d: + if self.is_instrument == "SANS2D": script += "from instrument.sans2d import * # pylint: disable=wildcard-import, unused-wildcard-import\n" script += f"\ndef {scriptname}():\n" @@ -49,7 +46,7 @@ def generate_setup(self, settings): def check_positions(self, runs): script = "" - if self.is_larmor: + if self.is_instrument == "LARMOR": position_list = ",".join([exp.position for exp in runs if exp.position]) if position_list: script += self.get_indent_string(1) + f"if check_move_pos(['{position_list}']) != True:\n" @@ -119,9 +116,9 @@ def do_trans_run(self, script, exp, indent, settings): waitfor = "seconds" elif exp.trans_wait == "Minutes": waitfor = "minutes" - if self.is_larmor or self.is_zoom: + if self.is_instrument == "LARMOR" or self.is_instrument == "ZOOM": script += self.get_indent_string(indent) + f"do_trans(title='{fulltitle}', pos='{exp.position}', {waitfor}={exp.trans}, thickness={exp.thickness})\n" - elif self.is_loq: + elif self.is_instrument == "LOQ": script += self.get_indent_string(indent) + f"do_trans(title='{fulltitle}', pos='{exp.position}', {waitfor}={exp.trans}, thickness={exp.thickness}, aperture='small')\n" else: script += self.get_indent_string(indent) + f"do_trans(title='{fulltitle}', pos='{exp.position}', {waitfor}={exp.trans}, thickness={exp.thickness}, dae='transmission', aperture='{self.aperture_string(settings.a2_setting_trans)}')\n" @@ -147,7 +144,7 @@ def do_sans_run(self, script, exp, indent, settings): waitfor = "seconds" elif exp.sans_wait == "Minutes": waitfor = "minutes" - if self.is_larmor or self.is_zoom: + if self.is_instrument == "LARMOR" or self.is_instrument == "ZOOM": script += self.get_indent_string(indent) + f"do_sans(title='{fulltitle}', pos='{exp.position}', {waitfor}={exp.sans}, thickness={exp.thickness}, dae='{rtype}')\n" else: script += self.get_indent_string(indent) + f"do_sans(title='{fulltitle}', pos='{exp.position}', {waitfor}={exp.sans}, thickness={exp.thickness}, dae='{rtype}', aperture='{self.aperture_string(settings.a2_setting_sans)}')\n" @@ -241,7 +238,8 @@ def get_minutes(wait, length, count_rate): runs = [Experiment(position="pos1", trans=10, sans=20, trans_wait="Minutes", sans_wait="Minutes", sample="Sample1", thickness=1.0)] settings = ExperimentSettings(sample_width="10", sample_height="10", sample_geometry="Flat", order="AllTrans", num_trans=1, num_sans=1, collection_mode="Events", a2_setting_trans="Large", a2_setting_sans="Large") estimation = Estimation(count_rate=100, move_time=10) - generator = ScriptGenerator(is_larmor=True, is_loq=False, is_zoom=False, is_sans2d=False) + is_instrument = "LARMOR" + generator = ScriptGenerator(is_instrument) script = generator.generate_script(runs, settings, "my_script") print(script) estimated_time = generator.estimate_run_time(runs, settings, estimation) diff --git a/ViewModels/InstrumentVM.py b/ViewModels/InstrumentVM.py index b74bd98..f88b9e5 100644 --- a/ViewModels/InstrumentVM.py +++ b/ViewModels/InstrumentVM.py @@ -22,10 +22,7 @@ def __init__(self, settings, estimation): self.larmor_aperture_sizes = [A2Setting.LARMOR_LARGE,A2Setting.LARMOR_MEDIUM,A2Setting.LARMOR_SMALL] self.zoom_aperture_sizes = [A2Setting.LARGE, A2Setting.MEDIUM, A2Setting.SMALL] - self.is_larmor = BooleanVar(value=True) - self.is_loq = BooleanVar(value=False) - self.is_sans2d = BooleanVar(value=False) - self.is_zoom = BooleanVar(value=False) + self.is_instrument = StringVar(value="LARMOR") ''' self.is_larmor = False self.is_loq = False @@ -33,30 +30,25 @@ def __init__(self, settings, estimation): self.is_zoom = False ''' - self.is_larmor.trace_add("write", self.update_instrument) - self.is_loq.trace_add("write", self.update_instrument) - self.is_sans2d.trace_add("write", self.update_instrument) - self.is_zoom.trace_add("write", self.update_instrument) + self.is_instrument.trace_add("write", self.update_instrument) + self.is_instrument.trace_add("write", self.on_property_changed) self.update_instrument() def update_instrument(self, *args): - if self.is_larmor.get(): - self.set_instrument(True, False, False, False, "A1, S1 Setting:", self.larmor_aperture_sizes, 40) - elif self.is_loq.get(): - self.set_instrument(False, True, False, False, "A2 Setting:", self.loq_aperture_sizes, 170) - elif self.is_sans2d.get(): - self.set_instrument(False, False, True, False, "A6 Setting:", self.sans2d_aperture_sizes, 40) - elif self.is_zoom.get(): - self.set_instrument(False, False, False, True, "A6 Setting:", self.zoom_aperture_sizes, 40) - - def set_instrument(self, larmor, loq, sans2d, zoom, a2_label, a2_choices, count_rate): - self.is_larmor.set(larmor) - self.is_loq.set(loq) - self.is_sans2d.set(sans2d) - self.is_zoom.set(zoom) - - InstrumentInfo.script_generator = ScriptGenerator(larmor, loq, zoom, sans2d) + if self.is_instrument.get() == "LARMOR": + self.set_instrument(self.is_instrument.get(), "A1, S1 Setting:", self.larmor_aperture_sizes, 40) + elif self.is_instrument.get() == "LOQ": + self.set_instrument(self.is_instrument.get(), "A2 Setting:", self.loq_aperture_sizes, 170) + elif self.is_instrument.get() == "SANS2D": + self.set_instrument(self.is_instrument.get(), "A6 Setting:", self.sans2d_aperture_sizes, 40) + elif self.is_instrument.get() == "ZOOM": + self.set_instrument(self.is_instrument.get(), "A6 Setting:", self.zoom_aperture_sizes, 40) + + def set_instrument(self, instrument, a2_label, a2_choices, count_rate): + self.is_instrument.set(instrument) + + InstrumentInfo.script_generator = ScriptGenerator(instrument) InstrumentInfo.a2_label = a2_label InstrumentInfo.a2_choices = a2_choices @@ -67,7 +59,8 @@ def set_instrument(self, larmor, loq, sans2d, zoom, a2_label, a2_choices, count_ self.settings.a2_setting_sans = Settings.a2_setting_sans self.settings.a2_setting_trans = Settings.a2_setting_trans - + def on_property_changed(self, *args): + print(f"Property {args[0]} changed to "+ self.is_instrument.get()) if __name__ == "__main__": @@ -76,12 +69,5 @@ def set_instrument(self, larmor, loq, sans2d, zoom, a2_label, a2_choices, count_ estimation = Estimation() vm = InstrumentVM(settings, estimation) - def on_property_changed(name): - print(f"Property {name} changed") - - vm.is_larmor.trace_add("write", lambda *args: on_property_changed("is_larmor")) - vm.is_loq.trace_add("write", lambda *args: on_property_changed("is_loq")) - vm.is_sans2d.trace_add("write", lambda *args: on_property_changed("is_sans2d")) - vm.is_zoom.trace_add("write", lambda *args: on_property_changed("is_zoom")) - + vm.is_instrument = StringVar(value="loq") root.mainloop() \ No newline at end of file From 76a6355088cbe749ebeb07839b147cedc35e1970 Mon Sep 17 00:00:00 2001 From: Dirk Honecker Date: Fri, 27 Dec 2024 01:46:03 +0000 Subject: [PATCH 12/88] Fixing UnitTesting ScriptGenerator --- Models/Experiment.py | 42 +++++++++++++++---------------- Models/ExperimentSettings.py | 48 ++++++++++++++++++++++++++---------- Scripting/ScriptGenerator.py | 37 ++++++++++++++++----------- ViewModels/InstrumentVM.py | 2 +- 4 files changed, 79 insertions(+), 50 deletions(-) diff --git a/Models/Experiment.py b/Models/Experiment.py index 8aaefa0..5b9292f 100644 --- a/Models/Experiment.py +++ b/Models/Experiment.py @@ -2,26 +2,26 @@ class Experiment: - def __init__(self): - self._position = tk.StringVar(value='') - self._trans = tk.DoubleVar(value=0) - self._trans_wait = tk.StringVar(value='') - self._sans = tk.DoubleVar(value=0) - self._sans_wait = tk.StringVar(value='') - self._period = tk.StringVar(value='') - self._background = tk.StringVar(value='') - self._sample = tk.StringVar(value='') - self._thickness = tk.DoubleVar(value=0) - self._temperature1 = tk.StringVar(value='') - self._temperature2 = tk.StringVar(value='') - self._field = tk.StringVar(value='') - self._shear_rate1 = tk.StringVar(value='') - self._shear_rate2 = tk.StringVar(value='') - self._shear_angle1 = tk.StringVar(value='') - self._shear_angle2 = tk.StringVar(value='') - self._pre_command = tk.StringVar(value='') - self._post_command = tk.StringVar(value='') - self._rb_number = tk.StringVar(value='') + def __init__(self, position='', trans=0, trans_wait='', sans=0, sans_wait='', period='', background='', sample='', thickness=0, temperature1='', temperature2='', field='', shear_rate1='', shear_rate2='', shear_angle1='', shear_angle2='', pre_command='', post_command='', rb_number=''): + self._position = tk.StringVar(value=position) + self._trans = tk.DoubleVar(value=trans) + self._trans_wait = tk.StringVar(value=trans_wait) + self._sans = tk.DoubleVar(value=sans) + self._sans_wait = tk.StringVar(value=sans_wait) + self._period = tk.StringVar(value=period) + self._background = tk.StringVar(value=background) + self._sample = tk.StringVar(value=sample) + self._thickness = tk.DoubleVar(value=thickness) + self._temperature1 = tk.StringVar(value=temperature1) + self._temperature2 = tk.StringVar(value=temperature2) + self._field = tk.StringVar(value=field) + self._shear_rate1 = tk.StringVar(value=shear_rate1) + self._shear_rate2 = tk.StringVar(value=shear_rate2) + self._shear_angle1 = tk.StringVar(value=shear_angle1) + self._shear_angle2 = tk.StringVar(value=shear_angle2) + self._pre_command = tk.StringVar(value=pre_command) + self._post_command = tk.StringVar(value=post_command) + self._rb_number = tk.StringVar(value=rb_number) self.property_changed = [] self._position.trace_add("write", self.on_property_changed) @@ -204,7 +204,7 @@ def rb_number(self, value): if __name__ == "__main__": def on_property_changed(name): print(f"Property {name} changed") - + root = tk.Tk() experiment = Experiment() experiment.property_changed.append(on_property_changed) diff --git a/Models/ExperimentSettings.py b/Models/ExperimentSettings.py index 1a65435..42dbfe4 100644 --- a/Models/ExperimentSettings.py +++ b/Models/ExperimentSettings.py @@ -31,6 +31,30 @@ def __init__(self): self._sample_width.trace_add("write", self.on_property_changed) self._collection_mode.trace_add("write", self.on_property_changed) + def __call__(self, order=None, num_sans=None, num_trans=None, loop_per_run=None, a2_setting_sans=None, a2_setting_trans=None, sample_geometry=None, sample_height=None, sample_width=None, collection_mode=None, is_valid=None): + if order is not None: + self.order = order + if num_sans is not None: + self.num_sans = num_sans + if num_trans is not None: + self.num_trans = num_trans + if loop_per_run is not None: + self.loop_per_run = loop_per_run + if a2_setting_sans is not None: + self.a2_setting_sans = a2_setting_sans + if a2_setting_trans is not None: + self.a2_setting_trans = a2_setting_trans + if sample_geometry is not None: + self.sample_geometry = sample_geometry + if sample_height is not None: + self.sample_height = sample_height + if sample_width is not None: + self.sample_width = sample_width + if collection_mode is not None: + self.collection_mode = collection_mode + if is_valid is not None: + self.is_valid = is_valid + def on_property_changed(self, *args): for callback in self.property_changed: callback(args[0]) @@ -38,19 +62,17 @@ def on_property_changed(self, *args): def save(self): settings = Settings() - settings.run_order(str(self._order.get())) - settings.num_sans(str(self._num_sans.get())) - settings.num_sans(str(self._num_trans.get())) - settings.loop_per_run(str(self._loop_per_run.get())) - print(self._a2_setting_sans.get()) - settings.a2_setting_sans(self._a2_setting_sans.get()) - settings.a2_setting_trans(self._a2_setting_trans.get()) - settings.geometry(self._sample_geometry.get()) - settings.sample_height(str(self._sample_height.get())) - settings.sample_width(str(self._sample_width.get())) - settings.collection_mode(self._collection_mode.get()) - with open(self.config_file, 'w') as configfile: - self.config.write(configfile) + settings.run_order = self.order + settings.num_sans = self.num_sans + settings.num_trans = self.num_trans + settings.loop_per_run = self.loop_per_run + settings.a2_setting_sans = self.a2_setting_sans + settings.a2_setting_trans = self.a2_setting_trans + settings.geometry = self.sample_geometry + settings.sample_height = self.sample_height + settings.sample_width = self.sample_width + settings.collection_mode = self.collection_mode + settings.save() @property def order(self): diff --git a/Scripting/ScriptGenerator.py b/Scripting/ScriptGenerator.py index 9f7abe9..ebe6db8 100644 --- a/Scripting/ScriptGenerator.py +++ b/Scripting/ScriptGenerator.py @@ -1,4 +1,6 @@ -from datetime import datetime, timedelta +import tkinter as tk + +from datetime import datetime, timedelta from Models.Estimation import Estimation from Models.Experiment import Experiment from Models.ExperimentSettings import ExperimentSettings @@ -7,7 +9,8 @@ class ScriptGenerator: def __init__(self, instrument): self.indent = " " - self.is_instrument = instrument + self.is_instrument = tk.StringVar() # Initialize the attribute + self.is_instrument.set(instrument) def generate_script(self, runs, settings, scriptname): script = f"# Script created by SANSScript at {datetime.now()}\n\n" @@ -25,13 +28,13 @@ def generate_header(self, scriptname): script += "from genie_python import genie as g, BLOCK_NAMES as b\n" script += "sys.path.append(r\"c:\\instrument\\scripts\")\n" - if self.is_instrument == "LARMOR": + if self.is_instrument.get() == "LARMOR": script += "from instrument.larmor import * # pylint: disable=wildcard-import, unused-wildcard-import\n" - if self.is_instrument == "LOQ": + if self.is_instrument.get() == "LOQ": script += "from instrument.loq import * # pylint: disable=wildcard-import, unused-wildcard-import\n" - if self.is_instrument == "ZOOM": + if self.is_instrument.get() == "ZOOM": script += "from instrument.zoom import * # pylint: disable=wildcard-import, unused-wildcard-import\n" - if self.is_instrument == "SANS2D": + if self.is_instrument.get() == "SANS2D": script += "from instrument.sans2d import * # pylint: disable=wildcard-import, unused-wildcard-import\n" script += f"\ndef {scriptname}():\n" @@ -46,7 +49,7 @@ def generate_setup(self, settings): def check_positions(self, runs): script = "" - if self.is_instrument == "LARMOR": + if self.is_instrument.get() == "LARMOR": position_list = ",".join([exp.position for exp in runs if exp.position]) if position_list: script += self.get_indent_string(1) + f"if check_move_pos(['{position_list}']) != True:\n" @@ -116,9 +119,9 @@ def do_trans_run(self, script, exp, indent, settings): waitfor = "seconds" elif exp.trans_wait == "Minutes": waitfor = "minutes" - if self.is_instrument == "LARMOR" or self.is_instrument == "ZOOM": + if self.is_instrument.get() == "LARMOR" or self.is_instrument.get() == "ZOOM": script += self.get_indent_string(indent) + f"do_trans(title='{fulltitle}', pos='{exp.position}', {waitfor}={exp.trans}, thickness={exp.thickness})\n" - elif self.is_instrument == "LOQ": + elif self.is_instrument.get() == "LOQ": script += self.get_indent_string(indent) + f"do_trans(title='{fulltitle}', pos='{exp.position}', {waitfor}={exp.trans}, thickness={exp.thickness}, aperture='small')\n" else: script += self.get_indent_string(indent) + f"do_trans(title='{fulltitle}', pos='{exp.position}', {waitfor}={exp.trans}, thickness={exp.thickness}, dae='transmission', aperture='{self.aperture_string(settings.a2_setting_trans)}')\n" @@ -144,7 +147,7 @@ def do_sans_run(self, script, exp, indent, settings): waitfor = "seconds" elif exp.sans_wait == "Minutes": waitfor = "minutes" - if self.is_instrument == "LARMOR" or self.is_instrument == "ZOOM": + if self.is_instrument.get() == "LARMOR" or self.is_instrument.get() == "ZOOM": script += self.get_indent_string(indent) + f"do_sans(title='{fulltitle}', pos='{exp.position}', {waitfor}={exp.sans}, thickness={exp.thickness}, dae='{rtype}')\n" else: script += self.get_indent_string(indent) + f"do_sans(title='{fulltitle}', pos='{exp.position}', {waitfor}={exp.sans}, thickness={exp.thickness}, dae='{rtype}', aperture='{self.aperture_string(settings.a2_setting_sans)}')\n" @@ -235,11 +238,15 @@ def get_minutes(wait, length, count_rate): if __name__ == "__main__": - runs = [Experiment(position="pos1", trans=10, sans=20, trans_wait="Minutes", sans_wait="Minutes", sample="Sample1", thickness=1.0)] - settings = ExperimentSettings(sample_width="10", sample_height="10", sample_geometry="Flat", order="AllTrans", num_trans=1, num_sans=1, collection_mode="Events", a2_setting_trans="Large", a2_setting_sans="Large") - estimation = Estimation(count_rate=100, move_time=10) - is_instrument = "LARMOR" - generator = ScriptGenerator(is_instrument) + root = tk.Tk() + experiment=Experiment() + runs = [experiment(position="pos1", trans=10, sans=20, trans_wait="Minutes", sans_wait="Minutes", sample="Sample1", thickness=1.0)] + experiment_settings =ExperimentSettings() + settings = experiment_settings(sample_width="10", sample_height="10", sample_geometry="Flat", order="AllTrans", num_trans=1, num_sans=1, collection_mode="Events", a2_setting_trans="Large", a2_setting_sans="Large") + estimate=Estimation() + estimation = estimate(count_rate=100, move_time=10) + instrument = "LARMOR" + generator = ScriptGenerator(instrument) script = generator.generate_script(runs, settings, "my_script") print(script) estimated_time = generator.estimate_run_time(runs, settings, estimation) diff --git a/ViewModels/InstrumentVM.py b/ViewModels/InstrumentVM.py index f88b9e5..4ff6867 100644 --- a/ViewModels/InstrumentVM.py +++ b/ViewModels/InstrumentVM.py @@ -69,5 +69,5 @@ def on_property_changed(self, *args): estimation = Estimation() vm = InstrumentVM(settings, estimation) - vm.is_instrument = StringVar(value="loq") + vm.is_instrument.set('loq') root.mainloop() \ No newline at end of file From cb5feea8b7e33ab99098c1c17b36625e8da642b1 Mon Sep 17 00:00:00 2001 From: Dirk Honecker Date: Fri, 27 Dec 2024 01:46:32 +0000 Subject: [PATCH 13/88] Fixing and UnitTesting ScriptGenerator --- Properties/Settings.ini | 14 ++--- TableTest.py | 111 ++++++++++++++++++++++++++++++++++++++++ TextFile2.py | 28 ++++++++++ 3 files changed, 146 insertions(+), 7 deletions(-) create mode 100644 TableTest.py create mode 100644 TextFile2.py diff --git a/Properties/Settings.ini b/Properties/Settings.ini index 9fff4e1..9d42e03 100644 --- a/Properties/Settings.ini +++ b/Properties/Settings.ini @@ -1,15 +1,15 @@ [DEFAULT] lastinstrument = LOQ -sampleheight = 11 -samplewidth = 11 -geometry = Flat Plate -runorder = Alternate - TRANS first +sampleheight = 10.0 +samplewidth = 10.0 +geometry = Flat +runorder = AllTrans numtrans = 1 numsans = 1 loopperrun = False -a2settingsans = -a2settingtrans = -collectionmode = 'Events' +a2settingsans = Large +a2settingtrans = Large +collectionmode = Events tableentries = 'POSITION, TRANS, TRANS_WAIT, SANS, SANS_WAIT, PERIOD, SAMPLE_IDENTIFICATION_STRING, BACKGROUND, THICKNESS, TEMPERATURE, TEMPERATURE2, FIELD, SHEAR_RATE_1, SHEAR_RATE_2, SHEAR_ANGLE_1, SHEAR_ANGLE_2, PRE_COMMAND, POST_COMMAND, RB_NUMBER' count_rate = 38 move_time = 120 diff --git a/TableTest.py b/TableTest.py new file mode 100644 index 0000000..6e6d202 --- /dev/null +++ b/TableTest.py @@ -0,0 +1,111 @@ + +''' +import tkinter as tk + +class SimpleTableInput(tk.Frame): + def __init__(self, parent, rows, columns): + tk.Frame.__init__(self, parent) + + self._entry = {} + self.rows = rows + self.columns = columns + + # register a command to use for validation + vcmd = (self.register(self._validate), "%P") + + # create the table of widgets + for row in range(self.rows): + for column in range(self.columns): + index = (row, column) + e = tk.Entry(self, validate="key", validatecommand=vcmd) + e.grid(row=row, column=column, stick="nsew") + self._entry[index] = e + # adjust column weights so they all expand equally + for column in range(self.columns): + self.grid_columnconfigure(column, weight=1) + # designate a final, empty row to fill up any extra space + self.grid_rowconfigure(rows, weight=1) + + def get(self): + '''Return a list of lists, containing the data in the table''' + result = [] + for row in range(self.rows): + current_row = [] + for column in range(self.columns): + index = (row, column) + current_row.append(self._entry[index].get()) + result.append(current_row) + return result + + def _validate(self, P): + '''Perform input validation. + + Allow only an empty value, or a value that can be converted to a float + ''' + if P.strip() == "": + return True + + try: + f = float(P) + except ValueError: + self.bell() + return False + return True + +class Example(tk.Frame): + def __init__(self, parent): + tk.Frame.__init__(self, parent) + self.table = SimpleTableInput(self, 3, 4) + self.submit = tk.Button(self, text="Submit", command=self.on_submit) + self.table.pack(side="top", fill="both", expand=True) + self.submit.pack(side="bottom") + + def on_submit(self): + print(self.table.get()) + + +root = tk.Tk() +Example(root).pack(side="top", fill="both", expand=True) +root.mainloop() +''' + + +''' +move to next column and row with enter and tab +import tkinter as tk + +class Example(): + def __init__(self): + self.root = tk.Tk() + self.table = tk.Frame(self.root) + self.table.pack(fill="both", expand=True) + + self.rows = [] + for row in range(10): + row_entries = [] + self.rows.append(row_entries) + for column in range(4): + entry = tk.Entry(self.table) + entry.grid(row=row, column=column) + row_entries.append(entry) + + entry.bind("", self.handle_enter) + + def handle_enter(self, event): + # get the row and column of the entry that got the event + entry = event.widget + row = int(entry.grid_info()['row']) + column = int(entry.grid_info()['column']) + + # compute the new row; either the next row or circle + # back around to the first row + new_row = row+1 if row+1 < len(self.rows) else 0 + + # get the entry for the new row, and set focus to it + entry = self.rows[new_row][column] + entry.focus_set() + +example = Example() +tk.mainloop() +''' + diff --git a/TextFile2.py b/TextFile2.py new file mode 100644 index 0000000..d64e6a0 --- /dev/null +++ b/TextFile2.py @@ -0,0 +1,28 @@ +import tkinter as tk +from tkinter import ttk, filedialog, messagebox +from tkinter.scrolledtext import ScrolledText +import pandas as pd +from Models.DataCollection import DataCollection +from ViewModels.DataGridVM import DataGridVM +from ViewModels.SettingsPanelVM import SettingsPanelVM +from ViewModels.InstrumentVM import InstrumentVM +from Models.InstrumentInfo import InstrumentInfo +from Models.Estimation import Estimation +from Models.ExperimentSettings import ExperimentSettings +from IO.ExcelIO import ExcelIO +from IO.CsvExporter import CsvExporter +from Dialogs.PreviewDialog import PreviewDialog +from SettingsPanel import SettingsPanel +from EstimationPanel import EstimationPanel +from TableWidget import TableWidget +from Properties.Settings import Settings +from Scripting.ScriptGenerator import ScriptGenerator + +from datetime import datetime, timedelta +from Models.Estimation import Estimation +from Models.Experiment import Experiment +from Models.ExperimentSettings import ExperimentSettings +from Models.A2Setting import A2Setting + + + From 264b63e5c751b2df4f16de42bf2170a57f0c0a92 Mon Sep 17 00:00:00 2001 From: Dirk Honecker Date: Mon, 30 Dec 2024 04:20:14 +0000 Subject: [PATCH 14/88] Complete GUI Table add instrument specific choices and DAE mode --- App.config | 48 -------------- EstimationPanel.py | 2 +- IO/CsvExporter.py | 4 +- IO/ExcelIO.py | 5 +- MainWindow.py | 37 ++++++----- Models/A2Setting.py | 42 ++++++++++-- Models/DataCollection.py | 35 ++++++++-- Models/Estimation.py | 8 +-- Models/Experiment.py | 2 +- Models/ExperimentSettings.py | 106 +++++++++++++++++++++--------- Models/InstrumentInfo.py | 55 +++++++++------- Properties/Settings.ini | 16 ++--- Properties/Settings.py | 61 ++++++++--------- Scripting/ScriptGenerator.py | 13 ++-- SettingsPanel.py | 119 ++++++++++++++++++++++++++-------- TableDataConstructor.py | 16 ++--- TableWidget.py | 76 +++++++++++----------- ViewModels/InstrumentVM.py | 61 ++++------------- ViewModels/SettingsPanelVM.py | 13 ++-- app_config.ini | 15 ----- 20 files changed, 408 insertions(+), 326 deletions(-) delete mode 100644 App.config delete mode 100644 app_config.ini diff --git a/App.config b/App.config deleted file mode 100644 index 2f23160..0000000 --- a/App.config +++ /dev/null @@ -1,48 +0,0 @@ - - - - -
- - - - - - - - - LARMOR - - - 11 - - - 11 - - - Flat Plate - - - Alternate - TRANS first - - - 1 - - - 1 - - - False - - - Large = 25, 16 mm - - - Large = 25, 16 mm - - - Events - - - - diff --git a/EstimationPanel.py b/EstimationPanel.py index 064087e..e74f8e8 100644 --- a/EstimationPanel.py +++ b/EstimationPanel.py @@ -45,7 +45,7 @@ def create_widgets(self): ttk.Label(grid_frame, text="Est. script time:").grid(row=2, column=0, sticky=tk.E) script_time_frame = ttk.Frame(grid_frame) script_time_frame.grid(row=2, column=1, columnspan=2) - self.txt_estimate = ttk.Entry(script_time_frame, width=15, textvariable=self.estimate.script_time, state='readonly') + self.txt_estimate = ttk.Entry(script_time_frame, width=15, textvariable=self.estimate.script_time, state="readonly") self.txt_estimate.grid(row=0, column=0, padx=5, pady=5) # Calculate button diff --git a/IO/CsvExporter.py b/IO/CsvExporter.py index 7a759ef..4c86197 100644 --- a/IO/CsvExporter.py +++ b/IO/CsvExporter.py @@ -4,7 +4,7 @@ class CsvExporter: @staticmethod def export(file, runs): - with open(file, mode='w', newline='') as csvfile: + with open(file, mode="w", newline="") as csvfile: writer = csv.writer(csvfile) for exp in runs: row = [ @@ -36,4 +36,4 @@ def export(file, runs): runs = [ Experiment(position="pos1", trans=10, trans_wait="Minutes", sans=20, sans_wait="Minutes", period="1", sample="Sample1", background="Background1", thickness="1.0", temperature1="25", temperature2="30", field="1.5T", shear_rate1="100", shear_rate2="200", shear_angle1="45", shear_angle2="90", pre_command="Pre Command", post_command="Post Command", rb_number="RB12345") ] - CsvExporter.export('output.csv', runs) \ No newline at end of file + CsvExporter.export("output.csv", runs) \ No newline at end of file diff --git a/IO/ExcelIO.py b/IO/ExcelIO.py index 8b33798..89b9a40 100644 --- a/IO/ExcelIO.py +++ b/IO/ExcelIO.py @@ -17,6 +17,7 @@ def __init__(self): def open_excel_workbook(self, filename): try: self.dataset = pd.read_excel(filename, sheet_name=None) + except Exception as ex: messagebox.showerror("SANS Script - Error", str(ex)) raise IOError("Could not open Excel spreadsheet") @@ -77,8 +78,8 @@ def export_data_to_excel(self, runs, filename): Experiment(position="pos1", trans=10, trans_wait="Minutes", sans=20, sans_wait="Minutes", period="1", sample="Sample1", background="Background1", thickness="1.0", temperature1="25", temperature2="30", field="1.5T", shear_rate1="100", shear_rate2="200", shear_angle1="45", shear_angle2="90", pre_command="Pre Command", post_command="Post Command", rb_number="RB12345") ] excel_io = ExcelIO() - excel_io.export_data_to_excel(runs, 'output.xlsx') - excel_io.open_excel_workbook('output.xlsx') + excel_io.export_data_to_excel(runs, "output.xlsx") + excel_io.open_excel_workbook("output.xlsx") experiments = excel_io.extract_experiments() for exp in experiments: print(exp.__dict__) \ No newline at end of file diff --git a/MainWindow.py b/MainWindow.py index e3af20c..8af2463 100644 --- a/MainWindow.py +++ b/MainWindow.py @@ -1,11 +1,10 @@ import tkinter as tk from tkinter import ttk, filedialog, messagebox from tkinter.scrolledtext import ScrolledText -import pandas as pd from Models.DataCollection import DataCollection from ViewModels.DataGridVM import DataGridVM -from ViewModels.SettingsPanelVM import SettingsPanelVM -from ViewModels.InstrumentVM import InstrumentVM +#from ViewModels.SettingsPanelVM import SettingsPanelVM +#from ViewModels.InstrumentVM import InstrumentVM from Models.InstrumentInfo import InstrumentInfo from Models.Estimation import Estimation from IO.ExcelIO import ExcelIO @@ -16,8 +15,9 @@ from TableWidget import TableWidget from Properties.Settings import Settings from Scripting.ScriptGenerator import ScriptGenerator +from Models.ExperimentSettings import ExperimentSettings -TableEntries = 'POSITION, TRANS, TRANS_WAIT, SANS, SANS_WAIT, PERIOD, SAMPLE_IDENTIFICATION_STRING, BACKGROUND, THICKNESS, TEMPERATURE, TEMPERATURE2, FIELD, SHEAR_RATE_1, SHEAR_RATE_2, SHEAR_ANGLE_1, SHEAR_ANGLE_2, PRE_COMMAND, POST_COMMAND, RB_NUMBER' +TableEntries = "POSITION, TRANS, TRANS_WAIT, SANS, SANS_WAIT, PERIOD, SAMPLE_IDENTIFICATION_STRING, BACKGROUND, THICKNESS, TEMPERATURE, TEMPERATURE2, FIELD, SHEAR_RATE_1, SHEAR_RATE_2, SHEAR_ANGLE_1, SHEAR_ANGLE_2, PRE_COMMAND, POST_COMMAND, RB_NUMBER" class MainWindow(tk.Frame): @@ -29,9 +29,11 @@ def __init__(self,parent): self.data = DataGridVM() self.inst_info = InstrumentInfo() - self.settings = SettingsPanelVM(self.inst_info) + self.settings = SettingsPanel(self, inst_info=self.inst_info) + self.settings.experiment = ExperimentSettings() + self.estimate = Estimation() - self.instrument = InstrumentVM(self.settings.experiment, self.estimate) + #self.instrument = InstrumentVM(self.settings.experiment, self.estimate) self.create_widgets() self.load_last_instrument() @@ -50,10 +52,10 @@ def create_widgets(self): #self.data_frame.grid(row=0, column=1, rowspan=4, sticky="nsew", padx=5, pady=5) self.data_frame.grid(row=0, column=1, sticky="nsew", padx=5, pady=5) - ''' + """ for col in self.data_grid["columns"]: self.data_grid.heading(col, text=col.replace("_", " ")) - ''' + """ # Settings Panel self.pnl_settings = SettingsPanel(self, self.settings) @@ -65,12 +67,13 @@ def create_widgets(self): self.pnl_estimate.grid(row=1, column=0, sticky="ew") self.pnl_estimate.on_calculate_clicked = self.pnl_estimate_on_calculate_clicked # Buttons - self.btn_create_new_table = ttk.Button(self, text="Create New Table", command=self.btn_create_new_table_click) - self.btn_create_new_table.grid(row=2, column=0, padx=5, pady=5, sticky="w") - + button_frame = ttk.Frame(self) + button_frame.grid(row=2, column=0, padx=5, pady=5, sticky="w") + self.btn_create_new_table = ttk.Button(button_frame, text="Create New Table", command=self.btn_create_new_table_click) + self.btn_create_new_table.grid(row=0, column=0, padx=5, pady=5) - self.btn_open_excel = ttk.Button(self, text="Open Excel", command=self.btn_open_excel_click) - self.btn_open_excel.grid(row=3, column=0, padx=5, pady=5, sticky="w") + self.btn_open_excel = ttk.Button(button_frame, text="Open Excel", command=self.btn_open_excel_click) + self.btn_open_excel.grid(row=0, column=1, padx=5, pady=5) self.mnu_main = tk.Menu(self.parent) @@ -90,10 +93,10 @@ def create_widgets(self): instrument_menu.add_radiobutton(label="LOQ", variable=self.instrument_var, value="LOQ") instrument_menu.add_radiobutton(label="SANS2D", variable=self.instrument_var, value="SANS2D") instrument_menu.add_radiobutton(label="ZOOM", variable=self.instrument_var, value="ZOOM") - ''' + """ def onFrameConfigure(self, event): self.canvas.configure(scrollregion=self.canvas.bbox("all")) - ''' + """ def load_last_instrument(self): settings = Settings() inst = settings.last_instrument # Replace with actual settings retrieval @@ -105,10 +108,10 @@ def pnl_settings_on_write_clicked(self): filetypes = [("genie_python Files", "*.py")] if self.instrument_var.get() in ["LARMOR", "LOQ", "ZOOM", "SANS2D"] else [("Open Genie Files", "*.gcl")] filepath = filedialog.asksaveasfilename(defaultextension=filetypes[0][1], filetypes=filetypes) if filepath: - scriptname = filepath.split('/')[-1].replace(" ", "_").rsplit('.', 1)[0] + scriptname = filepath.split("/")[-1].replace(" ", "_").rsplit(".", 1)[0] script_generator= ScriptGenerator() script = script_generator.generate_script(self.data.runs, self.settings.experiment, scriptname) - with open(filepath, 'w') as file: + with open(filepath, "w") as file: file.write(script) def pnl_settings_on_preview_clicked(self): diff --git a/Models/A2Setting.py b/Models/A2Setting.py index 51591d5..0921902 100644 --- a/Models/A2Setting.py +++ b/Models/A2Setting.py @@ -8,11 +8,41 @@ class A2Setting: LARMOR_MEDIUM = "Medium = 20, 14 mm" LARMOR_SMALL = "Small = 15, 10 mm" + + LARMOR_choices = [ + LARMOR_LARGE, + LARMOR_MEDIUM, + LARMOR_SMALL + ] + + LOQ_choices = [ + LARGE, MEDIUM, SMALL + ] + + SANS2D_choices = [ + X_LARGE, LARGE, MEDIUM, SMALL + ] + + ZOOM_choices = [ + LARGE, MEDIUM, SMALL + ] + + @staticmethod + def get_LARMOR_choices(): + return A2Setting.LARMOR_choices + + @staticmethod + def get_LOQ_choices(): + return A2Setting.LOQ_choices + + @staticmethod + def get_SANS2D_choices(): + return A2Setting.SANS2D_choices + + @staticmethod + def get_ZOOM_choices(): + return A2Setting.ZOOM_choices + # Example usage if __name__ == "__main__": - print(A2Setting.X_LARGE) - print(A2Setting.LARGE) - print(A2Setting.MEDIUM) - print(A2Setting.SMALL) - print(A2Setting.TRANS) - print(A2Setting.LARMOR) \ No newline at end of file + print(A2Setting.get_LOQ_choices()) \ No newline at end of file diff --git a/Models/DataCollection.py b/Models/DataCollection.py index 1d3ed8c..27b81b5 100644 --- a/Models/DataCollection.py +++ b/Models/DataCollection.py @@ -5,18 +5,41 @@ TRANSSANS_EVENTS = "TRANSSANS_Events" TRANSSANS_HISTOGRAM = "TRANSSANS_Histogram" - choices = [ + LOQ_choices = [ HISTOGRAM, TRANSSANS_HISTOGRAM, EVENTS, - TRANSSANS_EVENTS, - SESANS + TRANSSANS_EVENTS ] + SANS2D_choices = [ + EVENTS + ] + + ZOOM_choices = [ + EVENTS + ] + + LARMOR_choices = [ + EVENTS, SESANS + ] + + @staticmethod + def get_LARMOR_choices(): + return DataCollection.LARMOR_choices + + @staticmethod + def get_LOQ_choices(): + return DataCollection.LOQ_choices + + @staticmethod + def get_SANS2D_choices(): + return DataCollection.SANS2D_choices + @staticmethod - def get_choices(): - return DataCollection.choices + def get_ZOOM_choices(): + return DataCollection.ZOOM_choices # Example usage if __name__ == "__main__": - print(DataCollection.get_choices()) \ No newline at end of file + print(DataCollection.get_ZOOM_choices()) \ No newline at end of file diff --git a/Models/Estimation.py b/Models/Estimation.py index 9d296b5..53b1689 100644 --- a/Models/Estimation.py +++ b/Models/Estimation.py @@ -4,11 +4,11 @@ class Estimation: - def __init__(self, count_rate=0, move_time=0, script_time = 0): + def __init__(self, count_rate=40, move_time=120, script_time = 0): - self._count_rate = tk.DoubleVar(value=40) - self._move_time = tk.DoubleVar(value=120) - self._script_time = tk.DoubleVar(value=0) + self._count_rate = tk.DoubleVar(value=count_rate) + self._move_time = tk.DoubleVar(value=move_time) + self._script_time = tk.DoubleVar(value=script_time) self._is_valid = tk.BooleanVar(value=True) diff --git a/Models/Experiment.py b/Models/Experiment.py index 5b9292f..1d5dbac 100644 --- a/Models/Experiment.py +++ b/Models/Experiment.py @@ -2,7 +2,7 @@ class Experiment: - def __init__(self, position='', trans=0, trans_wait='', sans=0, sans_wait='', period='', background='', sample='', thickness=0, temperature1='', temperature2='', field='', shear_rate1='', shear_rate2='', shear_angle1='', shear_angle2='', pre_command='', post_command='', rb_number=''): + def __init__(self, position="", trans=0, trans_wait="", sans=0, sans_wait="", period="", background="", sample="", thickness=0, temperature1="", temperature2="", field="", shear_rate1="", shear_rate2="", shear_angle1="", shear_angle2="", pre_command="", post_command="", rb_number=""): self._position = tk.StringVar(value=position) self._trans = tk.DoubleVar(value=trans) self._trans_wait = tk.StringVar(value=trans_wait) diff --git a/Models/ExperimentSettings.py b/Models/ExperimentSettings.py index 42dbfe4..4c65eeb 100644 --- a/Models/ExperimentSettings.py +++ b/Models/ExperimentSettings.py @@ -1,25 +1,36 @@ import tkinter as tk from Properties.Settings import Settings +from Models.A2Setting import A2Setting +from Models.DataCollection import DataCollection +from Models.InstrumentInfo import InstrumentInfo +from Models.Estimation import Estimation +from Properties.Settings import Settings class ExperimentSettings: - def __init__(self): - settings = Settings() - - self._order = tk.StringVar(value=settings.run_order) - self._num_sans = tk.IntVar(value=settings.num_sans) - self._num_trans = tk.IntVar(value=settings.num_trans) - self._loop_per_run = tk.BooleanVar(value=settings.loop_per_run) - self._a2_setting_sans = tk.StringVar(value=settings.a2_setting_sans) - self._a2_setting_trans = tk.StringVar(value=settings.a2_setting_trans) - self._sample_geometry = tk.StringVar(value=settings.geometry) - self._sample_height = tk.DoubleVar(value=settings.sample_height) - self._sample_width = tk.DoubleVar(value=settings.sample_width) + def __init__(self, settings=None, estimation=None): + self.settings = settings if settings is not None else Settings() + self.estimation = estimation if estimation is not None else Estimation() + + + self._order = tk.StringVar(value=self.settings.run_order) + self._num_sans = tk.IntVar(value=self.settings.num_sans) + self._num_trans = tk.IntVar(value=self.settings.num_trans) + self._loop_per_run = tk.BooleanVar(value=self.settings.loop_per_run) + self._a2_setting_sans = tk.StringVar(value=self.settings.a2_setting_sans) + self._a2_setting_trans = tk.StringVar(value=self.settings.a2_setting_trans) + self._sample_geometry = tk.StringVar(value=self.settings.geometry) + self._sample_height = tk.DoubleVar(value=self.settings.sample_height) + self._sample_width = tk.DoubleVar(value=self.settings.sample_width) self._sample_width_enabled = tk.BooleanVar(value=True) - - self._collection_mode = tk.StringVar(value=settings.collection_mode) + + self._collection_mode = tk.StringVar(value=self.settings.collection_mode) self._is_valid = tk.BooleanVar(value=True) self.property_changed = [] + self.is_instrument = tk.StringVar(value=self.settings.last_instrument) + + self.is_instrument.trace_add("write", self.update_instrument) + self.is_instrument.trace_add("write", self.on_property_changed) self._order.trace_add("write", self.on_property_changed) self._num_sans.trace_add("write", self.on_property_changed) self._num_trans.trace_add("write", self.on_property_changed) @@ -31,6 +42,16 @@ def __init__(self): self._sample_width.trace_add("write", self.on_property_changed) self._collection_mode.trace_add("write", self.on_property_changed) + self.larmor_aperture_sizes=A2Setting.get_LARMOR_choices() + self.loq_aperture_sizes=A2Setting.get_LOQ_choices() + self.sans2d_aperture_sizes=A2Setting.get_SANS2D_choices() + self.zoom_aperture_sizes=A2Setting.get_ZOOM_choices() + + self.larmor_data_collection=DataCollection.get_LARMOR_choices() + self.loq_data_collection=DataCollection.get_LOQ_choices() + self.sans2d_data_collection=DataCollection.get_SANS2D_choices() + self.zoom_data_collection=DataCollection.get_ZOOM_choices() + def __call__(self, order=None, num_sans=None, num_trans=None, loop_per_run=None, a2_setting_sans=None, a2_setting_trans=None, sample_geometry=None, sample_height=None, sample_width=None, collection_mode=None, is_valid=None): if order is not None: self.order = order @@ -51,7 +72,7 @@ def __call__(self, order=None, num_sans=None, num_trans=None, loop_per_run=None, if sample_width is not None: self.sample_width = sample_width if collection_mode is not None: - self.collection_mode = collection_mode + self.collection_mode = collection_mode if is_valid is not None: self.is_valid = is_valid @@ -60,19 +81,44 @@ def on_property_changed(self, *args): callback(args[0]) self.save() + def update_instrument(self, *args): + if self.is_instrument.get() == "LARMOR": + self.set_instrument(self.is_instrument.get(), "A1, S1 Setting:", self.larmor_aperture_sizes,"Data Acquisition:",self.larmor_data_collection, 40) + elif self.is_instrument.get() == "LOQ": + self.set_instrument(self.is_instrument.get(), "A2 Setting:", self.loq_aperture_sizes,"Data Acquisition:",self.loq_data_collection, 170) + elif self.is_instrument.get() == "SANS2D": + self.set_instrument(self.is_instrument.get(), "A6 Setting:", self.sans2d_aperture_sizes,"Data Acquisition:",self.sans2d_data_collection, 40) + elif self.is_instrument.get() == "ZOOM": + self.set_instrument(self.is_instrument.get(), "A6 Setting:", self.zoom_aperture_sizes,"Data Acquisition:",self.zoom_data_collection, 40) + + def set_instrument(self, instrument, a2_label, a2_choices, data_collection_label, data_collection_choices, count_rate): + if not hasattr(self, 'settings') or self.settings is None: + self.settings = Settings() + + self.settings.is_instrument.set(instrument) + + self.settings.a2_label = a2_label + self.settings.a2_choices = a2_choices + self.settings.data_collection_label = data_collection_label + self.settings.data_collection_choices = data_collection_choices + self.settings.a2_trans_enabled = True + self.settings.collection_mode_enabled = True + self.estimation.count_rate = count_rate + + def save(self): - settings = Settings() - settings.run_order = self.order - settings.num_sans = self.num_sans - settings.num_trans = self.num_trans - settings.loop_per_run = self.loop_per_run - settings.a2_setting_sans = self.a2_setting_sans - settings.a2_setting_trans = self.a2_setting_trans - settings.geometry = self.sample_geometry - settings.sample_height = self.sample_height - settings.sample_width = self.sample_width - settings.collection_mode = self.collection_mode - settings.save() + + self.settings.run_order = self.order + self.settings.num_sans = self.num_sans + self.settings.num_trans = self.num_trans + self.settings.loop_per_run = self.loop_per_run + self.settings.a2_setting_sans = self.a2_setting_sans + self.settings.a2_setting_trans = self.a2_setting_trans + self.settings.geometry = self.sample_geometry + self.settings.sample_height = self.sample_height + self.settings.sample_width = self.sample_width + self.settings.collection_mode = self.collection_mode + self.settings.save() @property def order(self): @@ -129,7 +175,7 @@ def sample_geometry(self): @sample_geometry.setter def sample_geometry(self, value): self._sample_geometry.set(value) - self.sample_width_enabled = value != 'Disc' + self.sample_width_enabled = value != "Disc" @property def sample_height(self): @@ -158,7 +204,7 @@ def sample_width_enabled(self, value): self._sample_width_enabled.set(value) if not value: self.sample_width = self.sample_height - + @property def collection_mode(self): return self._collection_mode.get() @@ -166,7 +212,7 @@ def collection_mode(self): @collection_mode.setter def collection_mode(self, value): self._collection_mode.set(value) - + @property def is_valid(self): return self._is_valid.get() diff --git a/Models/InstrumentInfo.py b/Models/InstrumentInfo.py index 53d55ce..1521f2a 100644 --- a/Models/InstrumentInfo.py +++ b/Models/InstrumentInfo.py @@ -1,7 +1,7 @@ import tkinter as tk from tkinter import StringVar, BooleanVar from typing import List - +from Properties.Settings import Settings class InstrumentInfo: property_changed = [] @@ -9,21 +9,30 @@ class InstrumentInfo: script_generator = None a2_label = "" a2_choices = [] + data_collection_label = "" + data_collection_choices = [] a2_trans_enabled = False - collection_mode_enabled = False + collection_mode_enabled = True - def __init__(self): + def __init__(self,settings=None): + self.settings = settings if settings is not None else Settings() self._a2_label = StringVar(value="A2 Setting:") self._a2_choices = tk.Variable(value=["Large", "Medium", "Small"]) + self._data_collection_label = StringVar(value="Data acquisition:") + self._data_collection_choices = tk.Variable(value=["Event", "SESANS"]) self._a2_trans_enabled = BooleanVar(value=True) self._collection_mode_enabled = BooleanVar(value=False) self._script_generator = None + self._a2_label.trace_add("write", self.on_property_changed) self._a2_choices.trace_add("write", self.on_property_changed) + self._data_collection_label.trace_add("write", self.on_property_changed) + self._data_collection_choices.trace_add("write", self.on_property_changed) self._a2_trans_enabled.trace_add("write", self.on_property_changed) self._collection_mode_enabled.trace_add("write", self.on_property_changed) + def on_property_changed(self, *args): for callback in self.property_changed: callback(args[0]) @@ -51,6 +60,24 @@ def a2_choices(self, value: List[str]): self._a2_choices.set(value) self.on_static_property_changed("a2_choices") + @property + def data_collection_label(self): + return self._data_collection_label.get() + + @data_collection_label.setter + def data_collection_label(self, value): + self._data_collection_label.set(value) + self.on_static_property_changed("data_collection_label") + + @property + def data_collection_choices(self): + return self._data_collection_choices.get() + + @data_collection_choices.setter + def data_collection_choices(self, value: List[str]): + self._data_collection_choices.set(value) + self.on_static_property_changed("data_collection_choices") + @property def a2_trans_enabled(self): return self._a2_trans_enabled.get() @@ -69,28 +96,10 @@ def collection_mode_enabled(self, value): self._collection_mode_enabled.set(value) self.on_static_property_changed("collection_mode_enabled") -''' - @property - def script_generator(self): - self._script_generator = GeniePythonScriptGenerator() - return self._script_generator - - @script_generator.setter - def script_generator(self, value): - self._script_generator = value -''' -''' - class ScriptGenerator: - @staticmethod - def generate_script(runs, experiment, scriptname): - return f"# Script for {scriptname}\n# Runs: {runs}\n# Experiment: {experiment}" - - @staticmethod - def estimate_run_time(runs, experiment, estimate): - return len(runs) * 10 # Dummy implementation -''' + # Example usage if __name__ == "__main__": + root = tk.Tk() def on_property_changed(name): print(f"Property {name} changed") diff --git a/Properties/Settings.ini b/Properties/Settings.ini index 9d42e03..b5b631d 100644 --- a/Properties/Settings.ini +++ b/Properties/Settings.ini @@ -2,15 +2,15 @@ lastinstrument = LOQ sampleheight = 10.0 samplewidth = 10.0 -geometry = Flat -runorder = AllTrans +geometry = Flat Plate +runorder = All Trans First numtrans = 1 numsans = 1 loopperrun = False -a2settingsans = Large -a2settingtrans = Large -collectionmode = Events -tableentries = 'POSITION, TRANS, TRANS_WAIT, SANS, SANS_WAIT, PERIOD, SAMPLE_IDENTIFICATION_STRING, BACKGROUND, THICKNESS, TEMPERATURE, TEMPERATURE2, FIELD, SHEAR_RATE_1, SHEAR_RATE_2, SHEAR_ANGLE_1, SHEAR_ANGLE_2, PRE_COMMAND, POST_COMMAND, RB_NUMBER' -count_rate = 38 -move_time = 120 +a2settingsans = +a2settingtrans = +collectionmode = +tableentries = "POSITION, TRANS, TRANS_WAIT, SANS, SANS_WAIT, PERIOD, SAMPLE_IDENTIFICATION_STRING, BACKGROUND, THICKNESS, TEMPERATURE, TEMPERATURE2, FIELD, SHEAR_RATE_1, SHEAR_RATE_2, SHEAR_ANGLE_1, SHEAR_ANGLE_2, PRE_COMMAND, POST_COMMAND, RB_NUMBER" +countrate = 38 +movetime = 120 diff --git a/Properties/Settings.py b/Properties/Settings.py index 0b9b36f..2b2e2eb 100644 --- a/Properties/Settings.py +++ b/Properties/Settings.py @@ -2,136 +2,137 @@ import os class Settings: - def __init__(self, filename='Settings.ini'): + def __init__(self, filename="Settings.ini"): self.dict_settings = configparser.ConfigParser() directory=os.path.dirname(os.path.abspath(__file__)) - self.setting_ini_path = os.path.join(directory,filename) + self.setting_ini_path = os.path.join(directory,filename) + print(self.setting_ini_path) self.dict_settings.read(self.setting_ini_path) @property def last_instrument(self): - return self.dict_settings.get('DEFAULT','LastInstrument') + return self.dict_settings.get("DEFAULT","LastInstrument") @last_instrument.setter def last_instrument(self, value): - self.dict_settings.set('DEFAULT','LastInstrument', str(value)) + self.dict_settings.set("DEFAULT","LastInstrument", str(value)) self.save() @property def sample_height(self): - return self.dict_settings.get('DEFAULT','SampleHeight') + return self.dict_settings.get("DEFAULT","SampleHeight") @sample_height.setter def sample_height(self, value): - self.dict_settings.set('DEFAULT','SampleHeight', str(value)) + self.dict_settings.set("DEFAULT","SampleHeight", str(value)) self.save() @property def sample_width(self): - return self.dict_settings.get('DEFAULT','SampleWidth') + return self.dict_settings.get("DEFAULT","SampleWidth") @sample_width.setter def sample_width(self, value): - self.dict_settings.set('DEFAULT','SampleWidth', str(value)) + self.dict_settings.set("DEFAULT","SampleWidth", str(value)) self.save() @property def geometry(self): - return self.dict_settings.get('DEFAULT','Geometry') + return self.dict_settings.get("DEFAULT","Geometry") @geometry.setter def geometry(self, value): - self.dict_settings.set('DEFAULT','Geometry', str(value)) + self.dict_settings.set("DEFAULT","Geometry", str(value)) self.save() @property def run_order(self): - return self.dict_settings.get('DEFAULT','RunOrder') + return self.dict_settings.get("DEFAULT","RunOrder") @run_order.setter def run_order(self, value): - self.dict_settings.set('DEFAULT','RunOrder', str(value)) + self.dict_settings.set("DEFAULT","RunOrder", str(value)) self.save() @property def num_trans(self): - return self.dict_settings.get('DEFAULT','NumTrans') + return self.dict_settings.get("DEFAULT","NumTrans") @num_trans.setter def num_trans(self, value): - self.dict_settings.set('DEFAULT','NumTrans', str(value)) + self.dict_settings.set("DEFAULT","NumTrans", str(value)) self.save() @property def num_sans(self): - return self.dict_settings.get('DEFAULT','NumSans') + return self.dict_settings.get("DEFAULT","NumSans") @num_sans.setter def num_sans(self, value): - self.dict_settings.set('DEFAULT','NumSans', str(value)) + self.dict_settings.set("DEFAULT","NumSans", str(value)) self.save() @property def loop_per_run(self): - return self.dict_settings.get('DEFAULT','LoopPerRun') + return self.dict_settings.get("DEFAULT","LoopPerRun") @loop_per_run.setter def loop_per_run(self, value): - self.dict_settings.set('DEFAULT','LoopPerRun', str(value)) + self.dict_settings.set("DEFAULT","LoopPerRun", str(value)) self.save() @property def a2_setting_sans(self): - return self.dict_settings.get('DEFAULT','A2SettingSans') + return self.dict_settings.get("DEFAULT","A2SettingSans") @a2_setting_sans.setter def a2_setting_sans(self, value): - self.dict_settings.set('DEFAULT','A2SettingSans', str(value)) + self.dict_settings.set("DEFAULT","A2SettingSans", str(value)) self.save() @property def a2_setting_trans(self): - return self.dict_settings.get('DEFAULT','A2SettingTrans') + return self.dict_settings.get("DEFAULT","A2SettingTrans") @a2_setting_trans.setter def a2_setting_trans(self, value): - self.dict_settings.set('DEFAULT','A2SettingTrans', str(value)) + self.dict_settings.set("DEFAULT","A2SettingTrans", str(value)) self.save() @property def collection_mode(self): - return self.dict_settings.get('DEFAULT','CollectionMode') + return self.dict_settings.get("DEFAULT","CollectionMode") @collection_mode.setter def collection_mode(self, value): - self.dict_settings.set('DEFAULT','CollectionMode', str(value)) + self.dict_settings.set("DEFAULT","CollectionMode", str(value)) self.save() @property def count_rate(self): - return self.dict_settings.get('DEFAULT','count_rate') + return self.dict_settings.get("DEFAULT","CountRate") @count_rate.setter def count_rate(self, value): - self.dict_settings.set('DEFAULT','count_rate', str(value)) + self.dict_settings.set("DEFAULT","CountRate", str(value)) self.save() @property def move_time(self): - return self.dict_settings.get('DEFAULT','move_time') + return self.dict_settings.get("DEFAULT","MoveTime") @move_time.setter def move_time(self, value): - self.dict_settings.set('DEFAULT','move_time', str(value)) + self.dict_settings.set("DEFAULT","MoveTime", str(value)) self.save() def save(self): - with open(self.setting_ini_path, 'w') as configfile: + with open(self.setting_ini_path, "w") as configfile: self.dict_settings.write(configfile) # Example usage settings = Settings() print(settings.last_instrument) -settings.last_instrument = 'LOQ' +settings.last_instrument = "LOQ" print(settings.last_instrument) \ No newline at end of file diff --git a/Scripting/ScriptGenerator.py b/Scripting/ScriptGenerator.py index ebe6db8..597628c 100644 --- a/Scripting/ScriptGenerator.py +++ b/Scripting/ScriptGenerator.py @@ -5,6 +5,9 @@ from Models.Experiment import Experiment from Models.ExperimentSettings import ExperimentSettings from Models.A2Setting import A2Setting +from Models.RunOrder import RunOrder +from Models.DataCollection import DataCollection +from Models.A2Setting import A2Setting class ScriptGenerator: def __init__(self, instrument): @@ -62,7 +65,7 @@ def generate_runs(self, runs, settings): if not runs: return script - if settings.order == "AllTrans": + if settings.order == RunOrder.ALL_TRANS: script += self.get_indent_string(1) + f"for i in range({settings.num_trans}):\n" for exp in runs: if exp.trans: @@ -71,7 +74,7 @@ def generate_runs(self, runs, settings): for exp in runs: if exp.sans: script = self.do_sans_run(script, exp, 2, settings) - elif settings.order == "AllSans": + elif settings.order == RunOrder.ALL_SANS: script += self.get_indent_string(1) + f"for i in range({settings.num_sans}):\n" for exp in runs: if exp.sans: @@ -88,7 +91,7 @@ def generate_runs(self, runs, settings): script += self.get_indent_string(1) + "while True:\n" indent = 2 for exp in runs: - if settings.order == "TransFirst": + if settings.order == RunOrder.TRANS_FIRST: if exp.trans: script += self.get_indent_string(indent) + "if count < num_trans:\n" script = self.do_trans_run(script, exp, indent + 1, settings) @@ -111,8 +114,8 @@ def do_trans_run(self, script, exp, indent, settings): waitfor = "uamps" rtype = "histogram" fulltitle = f"{{{exp.sample}}}" - if settings.collection_mode == "Events": - rtype = "event" + if settings.collection_mode == DataCollection.EVENTS: + rtype = DataCollection.EVENTS if exp.trans_wait == "Frames": waitfor = "frames" elif exp.trans_wait == "Seconds": diff --git a/SettingsPanel.py b/SettingsPanel.py index 2199f0d..b141465 100644 --- a/SettingsPanel.py +++ b/SettingsPanel.py @@ -1,15 +1,30 @@ import tkinter as tk from tkinter import ttk -from ViewModels.SettingsPanelVM import SettingsPanelVM +#from ViewModels.SettingsPanelVM import SettingsPanelVM +#from ViewModels.InstrumentVM import InstrumentVM from Properties.Settings import Settings +from Models.RunOrder import RunOrder +from Models.Geometry import Geometry +from Models.ExperimentSettings import ExperimentSettings +from Models.InstrumentInfo import InstrumentInfo class SettingsPanel(ttk.Frame): - def __init__(self, parent, settings_vm=None): + #def __init__(self, parent, experiment=None, inst_info=None): + def __init__(self, parent,inst_info=None): super().__init__(parent) - - self.settings_vm = settings_vm if settings_vm is not None else SettingsPanelVM() + self.parent = parent + + self.inst_info = inst_info if inst_info is not None else InstrumentInfo() + #self.settings_vm = settings_vm if settings_vm is not None else SettingsPanelVM() + #self.instrument_vm = instrument_vm if instrument_vm is not None else InstrumentVM() self.on_preview_clicked = None self.on_write_clicked = None + self.property_changed = [] + #self.order_choices = RunOrder.get_choices() + #self.geometry_choices = Geometry.get_choices() + + #self._experiment = experiment if experiment is not None else ExperimentSettings() + #self._inst_info = inst_info if inst_info is not None else InstrumentInfo() self.create_widgets() @@ -19,7 +34,8 @@ def create_widgets(self): #self.write_button = ttk.Button(self, text="Write Script", command=self.on_write_clicked) #self.write_button.pack(side=tk.LEFT, padx=5, pady=5) - settings = Settings() + self.settings = Settings() + self.inst_info = InstrumentInfo() label = ttk.Label(self, text="Settings", font=("Helvetica", 16, "bold")) label.grid(row=0, column=0, columnspan=2, pady=10) @@ -29,21 +45,21 @@ def create_widgets(self): # Order ttk.Label(grid_frame, text="Order:").grid(row=0, column=0, sticky=tk.E) - print(self.settings_vm.order_choices) - print(settings.run_order) + print(self.order_choices) + print(self.settings.run_order) - self.com_order = ttk.Combobox(grid_frame, values=self.settings_vm.order_choices) - self.com_order['values']=self.settings_vm.order_choices + self.com_order = ttk.Combobox(grid_frame, values=self.order_choices) + self.com_order["values"]=self.order_choices self.com_order.grid(row=0, column=1, columnspan=2, padx=5, pady=5) - self.com_order.set(settings.run_order) # Set default value + self.com_order.set(self.settings.run_order) # Set default value ''' # Collection Mode - ttk.Label(grid_frame, text="Collection Mode:").grid(row=0, column=0, sticky=tk.E) + ttk.Label(grid_frame, text="Collection Mode:").grid(row=1, column=0, sticky=tk.E) print(self.settings_vm.collection_mode_choices) print(settings.collection_mode) self.collect_mode = ttk.Combobox(grid_frame, values=self.settings_vm.collection_mode_choices) - self.collect_mode['values']=self.settings_vm.collection_mode_choices + self.collect_mode["values"]=self.settings_vm.collection_mode_choices self.collect_mode.grid(row=1, column=1, columnspan=2, padx=5, pady=5) self.collect_mode.set(settings.collection_mode) # Set default value ''' @@ -53,7 +69,7 @@ def create_widgets(self): sans_frame = ttk.Frame(grid_frame) sans_frame.grid(row=1, column=1, columnspan=2) self.txt_times_sans = ttk.Entry(sans_frame, width=5) - self.txt_times_sans.insert(0, settings.num_sans) # Default value + self.txt_times_sans.insert(0, self.settings.num_sans) # Default value self.txt_times_sans.grid(row=0, column=0, padx=5, pady=5) ttk.Label(sans_frame, text="time(s)").grid(row=0, column=1) @@ -62,41 +78,61 @@ def create_widgets(self): trans_frame = ttk.Frame(grid_frame) trans_frame.grid(row=2, column=1, columnspan=2) self.txt_times_trans = ttk.Entry(trans_frame, width=5) - self.txt_times_trans.insert(0, settings.num_trans) # Default value + self.txt_times_trans.insert(0, self.settings.num_trans) # Default value self.txt_times_trans.grid(row=0, column=0, padx=5, pady=5) ttk.Label(trans_frame, text="time(s)").grid(row=0, column=1) # Loop over each run - self.chk_loop_run = ttk.Checkbutton(grid_frame, text="Loop over each run?") + self.loop_run_var = tk.BooleanVar(value=self.settings.loop_per_run) # Create BooleanVar and set default value + self.chk_loop_run = ttk.Checkbutton(grid_frame, text="Loop over each run?", variable=self.loop_run_var) self.chk_loop_run.grid(row=3, column=0, columnspan=3, pady=5) + # Collection Mode + tk.Label(grid_frame, text=self.inst_info.data_collection_label).grid(row=4, column=0, sticky=tk.E) + self.com_collection_mode = ttk.Combobox(grid_frame, values=self.inst_info.data_collection_choices) + self.com_collection_mode.grid(row=4, column=1, columnspan=2, padx=5, pady=5) + self.com_collection_mode.set(self.settings.collection_mode) # Set default value + + + # A2 SANS + tk.Label(grid_frame, text="SANS "+self.inst_info.a2_label).grid(row=5, column=0, sticky=tk.E) + self.com_a2_sans = ttk.Combobox(grid_frame, values=self.inst_info.a2_choices ) + self.com_a2_sans.grid(row=5, column=1, columnspan=2, padx=5, pady=5) + self.com_a2_sans.set(self.settings.a2_setting_sans) # Set default value + + # A2 TRANS + tk.Label(grid_frame, text="TRANS "+self.inst_info.a2_label).grid(row=6, column=0, sticky=tk.E) + self.com_a2_trans = ttk.Combobox(grid_frame, values=self.inst_info.a2_choices) + self.com_a2_trans.grid(row=6, column=1, columnspan=2, padx=5, pady=5) + self.com_a2_trans.set(self.settings.a2_setting_trans) # Set default value + # Sample Geometry - ttk.Label(grid_frame, text="Sample Geometry:").grid(row=6, column=0, sticky=tk.E) - self.com_sample_geo = ttk.Combobox(grid_frame, values=self.settings_vm.geometry_choices) - self.com_sample_geo.grid(row=6, column=1, columnspan=2, padx=5, pady=5) - self.com_sample_geo.set(settings.geometry) # Set default value + ttk.Label(grid_frame, text="Sample Geometry:").grid(row=7, column=0, sticky=tk.E) + self.com_sample_geo = ttk.Combobox(grid_frame, values=self.geometry_choices) + self.com_sample_geo.grid(row=7, column=1, columnspan=2, padx=5, pady=5) + self.com_sample_geo.set(self.settings.geometry) # Set default value # Sample Height - ttk.Label(grid_frame, text="Sample Height:").grid(row=7, column=0, sticky=tk.E) + ttk.Label(grid_frame, text="Sample Height:").grid(row=8, column=0, sticky=tk.E) sample_height_frame = ttk.Frame(grid_frame) - sample_height_frame.grid(row=7, column=1, columnspan=2) + sample_height_frame.grid(row=8, column=1, columnspan=2) self.txt_sample_height = ttk.Entry(sample_height_frame, width=5) - self.txt_sample_height.insert(0, settings.sample_height) # Default value + self.txt_sample_height.insert(0, self.settings.sample_height) # Default value self.txt_sample_height.grid(row=0, column=0, padx=5, pady=5) ttk.Label(sample_height_frame, text="mm").grid(row=0, column=1) # Sample Width - ttk.Label(grid_frame, text="Sample Width:").grid(row=8, column=0, sticky=tk.E) + ttk.Label(grid_frame, text="Sample Width:").grid(row=9, column=0, sticky=tk.E) sample_width_frame = ttk.Frame(grid_frame) - sample_width_frame.grid(row=8, column=1, columnspan=2) + sample_width_frame.grid(row=9, column=1, columnspan=2) self.txt_sample_width = ttk.Entry(sample_width_frame, width=5) - self.txt_sample_width.insert(0, settings.sample_width) # Default value + self.txt_sample_width.insert(0, self.settings.sample_width) # Default value self.txt_sample_width.grid(row=0, column=0, padx=5, pady=5) ttk.Label(sample_width_frame, text="mm").grid(row=0, column=1) # Buttons button_frame = ttk.Frame(self) - button_frame.grid(row=2, column=0, columnspan=2, pady=10) + button_frame.grid(row=10, column=0, columnspan=2, pady=10) self.btn_preview_script = ttk.Button(button_frame, text="Preview Script", command=self.btn_preview_script_click) self.btn_preview_script.grid(row=0, column=0, padx=5) self.btn_write_script = ttk.Button(button_frame, text="Write Script", command=self.btn_write_script_click) @@ -112,6 +148,37 @@ def btn_write_script_click(self): if self.on_write_clicked: self.on_write_clicked() + def on_property_changed(self, name): + for callback in self.property_changed: + callback(name) + + ''' + @property + def experiment(self): + return self._experiment + + @experiment.setter + def experiment(self, value): + self._experiment = value + self.on_property_changed("experiment") + + @property + def inst_info(self): + return self._inst_info + + @inst_info.setter + def inst_info(self, value): + self._inst_info = value + self.on_property_changed("inst_info") + ''' + @staticmethod + def order_choices(): + return RunOrder.get_choices() + + @staticmethod + def geometry_choices(): + return Geometry.get_choices() + if __name__ == "__main__": root = tk.Tk() diff --git a/TableDataConstructor.py b/TableDataConstructor.py index 313982a..9f04b99 100644 --- a/TableDataConstructor.py +++ b/TableDataConstructor.py @@ -1,17 +1,17 @@ class TableDataConstructor: def __init__(self, table_entries): - self.table_entries = table_entries.split(', ') + self.table_entries = table_entries.split(", ") def construct_empty_data(self): empty_data = { - 'rec1': {entry: '' for entry in self.table_entries} + "rec1": {entry: "" for entry in self.table_entries} } return empty_data def construct_data(self, data_list): data = {} for i, row in enumerate(data_list, start=1): - data[f'rec{i}'] = {entry: value for entry, value in zip(self.table_entries, row)} + data[f"rec{i}"] = {entry: value for entry, value in zip(self.table_entries, row)} return data def deconstruct_data(self, data_dict): @@ -22,20 +22,20 @@ def deconstruct_data(self, data_dict): return data_list # Example usage -TableEntries = 'POSITION, TRANS, TRANS_WAIT, SANS, SANS_WAIT, PERIOD, SAMPLE_IDENTIFICATION_STRING, BACKGROUND, THICKNESS, TEMPERATURE, TEMPERATURE2, FIELD, SHEAR_RATE_1, SHEAR_RATE_2, SHEAR_ANGLE_1, SHEAR_ANGLE_2, PRE_COMMAND, POST_COMMAND, RB_NUMBER' +TableEntries = "POSITION, TRANS, TRANS_WAIT, SANS, SANS_WAIT, PERIOD, SAMPLE_IDENTIFICATION_STRING, BACKGROUND, THICKNESS, TEMPERATURE, TEMPERATURE2, FIELD, SHEAR_RATE_1, SHEAR_RATE_2, SHEAR_ANGLE_1, SHEAR_ANGLE_2, PRE_COMMAND, POST_COMMAND, RB_NUMBER" constructor = TableDataConstructor(TableEntries) empty_data = constructor.construct_empty_data() print(empty_data) # Example usage -TableEntries = 'col1, col2, label, col3, col4, label2, col5, col6, label3' +TableEntries = "col1, col2, label, col3, col4, label2, col5, col6, label3" constructor = TableDataConstructor(TableEntries) # Construct data from a list of lists data_list = [ - [99.88, 108.79, 'rec1', 99.88, 108.79, 'rec1', 99.88, 108.79, 'rec1'], - [99.88, 108.79, 'rec2', 99.88, 108.79, 'rec2', 99.88, 108.79, 'rec2'], - [99.88, 108.79, 'rec3', 99.88, 108.79, 'rec3', 99.88, 108.79, 'rec3']] + [99.88, 108.79, "rec1", 99.88, 108.79, "rec1", 99.88, 108.79, "rec1"], + [99.88, 108.79, "rec2", 99.88, 108.79, "rec2", 99.88, 108.79, "rec2"], + [99.88, 108.79, "rec3", 99.88, 108.79, "rec3", 99.88, 108.79, "rec3"]] # Add more rows as needed data = constructor.construct_data(data_list) diff --git a/TableWidget.py b/TableWidget.py index ce17bcf..132309f 100644 --- a/TableWidget.py +++ b/TableWidget.py @@ -7,7 +7,7 @@ from collections import OrderedDict from TableDataConstructor import TableDataConstructor -TableEntries = 'POSITION, TRANS, TRANS_WAIT, SANS, SANS_WAIT, PERIOD, SAMPLE_IDENTIFICATION_STRING, BACKGROUND, THICKNESS, TEMPERATURE, TEMPERATURE2, FIELD, SHEAR_RATE_1, SHEAR_RATE_2, SHEAR_ANGLE_1, SHEAR_ANGLE_2, PRE_COMMAND, POST_COMMAND, RB_NUMBER' +TableEntries = "POSITION, TRANS, TRANS_WAIT, SANS, SANS_WAIT, PERIOD, SAMPLE_IDENTIFICATION_STRING, BACKGROUND, THICKNESS, TEMPERATURE, TEMPERATURE2, FIELD, SHEAR_RATE_1, SHEAR_RATE_2, SHEAR_ANGLE_1, SHEAR_ANGLE_2, PRE_COMMAND, POST_COMMAND, RB_NUMBER" data=None @@ -33,7 +33,7 @@ def popupFocusOut(event): # Default actions from the original class popupmenu.add_command(label="Sort by " + collabel, command=lambda: self.table.sortTable(currcol)) - popupmenu.add_command(label="Sort by " + collabel + ' (descending)', command=lambda: self.table.sortTable(currcol, reverse=1)) + popupmenu.add_command(label="Sort by " + collabel + " (descending)", command=lambda: self.table.sortTable(currcol, reverse=1)) #popupmenu.add_command(label="Delete This Column", command=self.table.deleteColumn) # Adding a custom command @@ -53,9 +53,9 @@ class TableImporter: def __init__(self): """Setup globals""" - self.separator_list = [',', ' ', '\t', ':'] + self.separator_list = [",", " ", "\t", ":"] self.var_sep = StringVar() - self.var_sep.set(',') + self.var_sep.set(",") return @staticmethod @@ -63,13 +63,13 @@ def ImportTableData(table, sep=None): """Import table data from a comma separated file and populate the table model""" parent = table.parentframe - filename = filedialog.askopenfilename(parent=parent, defaultextension='.csv', + filename = filedialog.askopenfilename(parent=parent, defaultextension=".csv", filetypes=[("CSV files", "*.csv")]) if not filename: return if sep is None: - sep = ',' + sep = "," if not os.path.isfile(filename): return None @@ -102,19 +102,19 @@ def show(self, callback=None): self.tablerowheader = CustomRowHeader(self.parentframe, self, width=self.rowheaderwidth) self.tablecolheader = CustomColumnHeader(self.parentframe, self) self.Yscrollbar = AutoScrollbar(self.parentframe,orient=VERTICAL,command=self.set_yviews) - self.Yscrollbar.grid(row=1,column=2,rowspan=1,sticky='news',pady=0,ipady=0) + self.Yscrollbar.grid(row=1,column=2,rowspan=1,sticky="news",pady=0,ipady=0) self.Xscrollbar = AutoScrollbar(self.parentframe,orient=HORIZONTAL,command=self.set_xviews) - self.Xscrollbar.grid(row=2,column=1,columnspan=1,sticky='news') - self['xscrollcommand'] = self.Xscrollbar.set - self['yscrollcommand'] = self.Yscrollbar.set - self.tablecolheader['xscrollcommand'] = self.Xscrollbar.set - self.tablerowheader['yscrollcommand'] = self.Yscrollbar.set + self.Xscrollbar.grid(row=2,column=1,columnspan=1,sticky="news") + self["xscrollcommand"] = self.Xscrollbar.set + self["yscrollcommand"] = self.Yscrollbar.set + self.tablecolheader["xscrollcommand"] = self.Xscrollbar.set + self.tablerowheader["yscrollcommand"] = self.Yscrollbar.set self.parentframe.rowconfigure(1,weight=1) self.parentframe.columnconfigure(1,weight=1) - self.tablecolheader.grid(row=0,column=1,rowspan=1,sticky='news',pady=0,ipady=0) - self.tablerowheader.grid(row=1,column=0,rowspan=1,sticky='news',pady=0,ipady=0) - self.grid(row=1,column=1,rowspan=1,sticky='news',pady=0,ipady=0) + self.tablecolheader.grid(row=0,column=1,rowspan=1,sticky="news",pady=0,ipady=0) + self.tablerowheader.grid(row=1,column=0,rowspan=1,sticky="news",pady=0,ipady=0) + self.grid(row=1,column=1,rowspan=1,sticky="news",pady=0,ipady=0) self.adjustColumnWidths() self.redrawTable(callback=callback) @@ -162,7 +162,7 @@ def fillDown(self, rowlist, collist): """Fill down a column, or multiple columns""" model = self.model #absrow = self.get_AbsoluteRow(rowlist[0]) - #remove first element as we don't want to overwrite it + #remove first element as we don"t want to overwrite it #rowlist.remove(rowlist[0]) return @@ -178,7 +178,7 @@ def display_sheet_data(self,name=None): sheet_name = name if sheet_name and self.workbook: sheet = self.workbook[sheet_name] - #TableEntries = 'col1, col2, label, col3, col4, label2, col5, col6, label3' + #TableEntries = "col1, col2, label, col3, col4, label2, col5, col6, label3" constructor = TableDataConstructor(TableEntries) data = constructor.construct_data(sheet.iter_rows(values_only=True)) self.data = data @@ -190,7 +190,7 @@ def importTable(self): self.importCSV() - def importCSV(self, filename=None, sep=','): + def importCSV(self, filename=None, sep=","): """Import from csv file""" importer = TableImporter() @@ -205,22 +205,22 @@ def do_bindings(self): self.bind("", self.handle_left_shift_click) self.bind("", self.handle_left_release) - if self.ostyp=='mac': + if self.ostyp=="mac": #For mac we bind Shift, left-click to right click self.bind("", self.handle_right_click) - self.bind('',self.handle_right_click) + self.bind("",self.handle_right_click) else: self.bind("", self.handle_right_click) - self.bind('', self.handle_mouse_drag) - self.bind('', self.handle_motion) + self.bind("", self.handle_mouse_drag) + self.bind("", self.handle_motion) self.bind_all("", self.deleteRow) self.bind_all("", self.addRow) self.bind_all("", self.clearData) self.bind_all("", self.paste) - #if not hasattr(self,'parentapp'): + #if not hasattr(self,"parentapp"): # self.parentapp = self.parentframe self.parentframe.master.bind_all("", self.handle_arrow_keys) @@ -232,16 +232,16 @@ def do_bindings(self): self.parentframe.master.bind_all("", self.handle_arrow_keys) self.parentframe.master.bind_all("", self.move_to_next_row) - #if 'windows' in self.platform: + #if "windows" in self.platform: self.bind("", self.mouse_wheel) - self.bind('', self.mouse_wheel) - self.bind('', self.mouse_wheel) + self.bind("", self.mouse_wheel) + self.bind("", self.mouse_wheel) self.focus_set() return def move_to_next_row(self, event): - """Move to the next row when enter is pressed and create a new row if it doesn't exist""" + """Move to the next row when enter is pressed and create a new row if it doesn"t exist""" current_row = self.getSelectedRow() if current_row >= self.rows - 1: self.addRow() @@ -249,7 +249,7 @@ def move_to_next_row(self, event): self.drawSelectedRect(self.currentrow, self.currentcol) self.drawSelectedRow() coltype = self.model.getColumnType(self.currentcol) - if coltype == 'text' or coltype == 'number': + if coltype == "text" or coltype == "number": self.drawCellEntry(self.currentrow, self.currentcol) def popupMenu(self, event, rows=None, cols=None, outside=None): @@ -258,8 +258,8 @@ def popupMenu(self, event, rows=None, cols=None, outside=None): # Default actions from the original class defaultactions = { - #"Set Fill Color": lambda: self.setcellColor(rows, cols, key='bg'), - #"Set Text Color": lambda: self.setcellColor(rows, cols, key='fg'), + #"Set Fill Color": lambda: self.setcellColor(rows, cols, key="bg"), + #"Set Text Color": lambda: self.setcellColor(rows, cols, key="fg"), "Copy": lambda: self.copyCell(rows, cols), "Paste": lambda: self.pasteCell(rows, cols), "Fill Down": lambda: self.fillDown(rows, cols), @@ -278,7 +278,7 @@ def popupMenu(self, event, rows=None, cols=None, outside=None): #defaultactions["Custom Command"] = self.customCommand main = [ "Copy", "Paste", "Fill Down", "Clear Data"] - general = ["Select All", "Add Row(s)", "Delete Row(s)", "Preferences",'New', 'Import Table', 'Export Table', 'Export Reductionbatch'] + general = ["Select All", "Add Row(s)", "Delete Row(s)", "Preferences","New", "Import Table", "Export Table", "Export Reductionbatch"] def createSubMenu(parent, label, commands): @@ -308,7 +308,7 @@ def popupFocusOut(event): def add_defaultcommands(): """now add general actions for all cells""" for action in main: - if action == 'Fill Down' and (rows == None or len(rows) <= 1): + if action == "Fill Down" and (rows == None or len(rows) <= 1): continue else: popupmenu.add_command(label=action, command=defaultactions[action]) @@ -333,7 +333,7 @@ def add_defaultcommands(): class TableWidget(Frame): """Frame for the table""" - #TableEntries = 'col1, col2, label, col3, col4, label2, col5, col6, label3' + #TableEntries = "col1, col2, label, col3, col4, label2, col5, col6, label3" constructor = TableDataConstructor(TableEntries) EMPTY_DATA = constructor.construct_empty_data() @@ -347,10 +347,10 @@ def __init__(self, parent=None, data=None): data = TableWidget.EMPTY_DATA self.data = data self.table = CustomTableCanvas(self, data=self.data) - #table.importCSV('test.csv') + #table.importCSV("test.csv") print (self.table.model.columnNames) - #table.model.data[1]['a'] = 'XX' - #table.model.setValueAt('YY',0,2) + #table.model.data[1]["a"] = "XX" + #table.model.setValueAt("YY",0,2) self.table.show() @@ -368,8 +368,8 @@ def customOption(self): if __name__ == "__main__": root = Tk() - root.geometry('800x500+200+100') - root.title('Test') + root.geometry("800x500+200+100") + root.title("Test") if data==None: data = TableWidget.EMPTY_DATA app = TableWidget(root, data=data) diff --git a/ViewModels/InstrumentVM.py b/ViewModels/InstrumentVM.py index 4ff6867..5231a96 100644 --- a/ViewModels/InstrumentVM.py +++ b/ViewModels/InstrumentVM.py @@ -1,6 +1,7 @@ import tkinter as tk from tkinter import StringVar, BooleanVar from Models.A2Setting import A2Setting +from Models.DataCollection import DataCollection from Models.InstrumentInfo import InstrumentInfo from Models.Estimation import Estimation from Models.ExperimentSettings import ExperimentSettings @@ -9,65 +10,27 @@ class InstrumentVM: - def __init__(self, settings, estimation): - #def __init__(self, experiment, estimate): - self.settings = settings - self.estimation = estimation - #self.estimate = estimate - #self.experiment = experiment - + def __init__(self, experimentsettings=None, estimation=None): + #def __init__(self, experiment, estimate): + self.settings = experimentsettings if experimentsettings is not None else ExperimentSettings() + self.estimation = estimation if estimation is not None else Estimation() - self.loq_aperture_sizes = [A2Setting.LARGE, A2Setting.MEDIUM, A2Setting.SMALL] - self.sans2d_aperture_sizes = [A2Setting.X_LARGE, A2Setting.LARGE, A2Setting.MEDIUM, A2Setting.SMALL] - self.larmor_aperture_sizes = [A2Setting.LARMOR_LARGE,A2Setting.LARMOR_MEDIUM,A2Setting.LARMOR_SMALL] - self.zoom_aperture_sizes = [A2Setting.LARGE, A2Setting.MEDIUM, A2Setting.SMALL] - self.is_instrument = StringVar(value="LARMOR") - ''' + + """ self.is_larmor = False self.is_loq = False self.is_sans2d = False self.is_zoom = False - ''' - - self.is_instrument.trace_add("write", self.update_instrument) - self.is_instrument.trace_add("write", self.on_property_changed) - - self.update_instrument() - - def update_instrument(self, *args): - if self.is_instrument.get() == "LARMOR": - self.set_instrument(self.is_instrument.get(), "A1, S1 Setting:", self.larmor_aperture_sizes, 40) - elif self.is_instrument.get() == "LOQ": - self.set_instrument(self.is_instrument.get(), "A2 Setting:", self.loq_aperture_sizes, 170) - elif self.is_instrument.get() == "SANS2D": - self.set_instrument(self.is_instrument.get(), "A6 Setting:", self.sans2d_aperture_sizes, 40) - elif self.is_instrument.get() == "ZOOM": - self.set_instrument(self.is_instrument.get(), "A6 Setting:", self.zoom_aperture_sizes, 40) - - def set_instrument(self, instrument, a2_label, a2_choices, count_rate): - self.is_instrument.set(instrument) - - InstrumentInfo.script_generator = ScriptGenerator(instrument) - - InstrumentInfo.a2_label = a2_label - InstrumentInfo.a2_choices = a2_choices - InstrumentInfo.a2_trans_enabled = True - InstrumentInfo.collection_mode_enabled = True - self.estimation.count_rate = count_rate - self.settings=Settings() - self.settings.a2_setting_sans = Settings.a2_setting_sans - self.settings.a2_setting_trans = Settings.a2_setting_trans - - def on_property_changed(self, *args): - print(f"Property {args[0]} changed to "+ self.is_instrument.get()) + """ + self.settings.update_instrument() if __name__ == "__main__": root = tk.Tk() - settings = ExperimentSettings() + experimentsettings = ExperimentSettings() estimation = Estimation() - vm = InstrumentVM(settings, estimation) + vm = InstrumentVM(experimentsettings, estimation) - vm.is_instrument.set('loq') + vm.is_instrument.set("loq") root.mainloop() \ No newline at end of file diff --git a/ViewModels/SettingsPanelVM.py b/ViewModels/SettingsPanelVM.py index 420385f..51fd0c9 100644 --- a/ViewModels/SettingsPanelVM.py +++ b/ViewModels/SettingsPanelVM.py @@ -9,14 +9,13 @@ from Models.Geometry import Geometry class SettingsPanelVM: - def __init__(self, inst_info=None): + def __init__(self, inst_info=None, experiment=None): self.property_changed = [] self.order_choices = RunOrder.get_choices() self.geometry_choices = Geometry.get_choices() - self.collection_mode_choices = DataCollection.get_choices() + #self.collection_mode_choices = DataCollection.get_choices() - - self._experiment = ExperimentSettings() + self._experiment = experiment if experiment is not None else ExperimentSettings() self._inst_info = inst_info if inst_info is not None else InstrumentInfo() def on_property_changed(self, name): @@ -48,11 +47,11 @@ def order_choices(): @staticmethod def geometry_choices(): return Geometry.get_choices() - + ''' @staticmethod def collection_mode_choices(): - return DataCollection.choices() - + return DataCollection.get_choices() + ''' diff --git a/app_config.ini b/app_config.ini deleted file mode 100644 index 38503b7..0000000 --- a/app_config.ini +++ /dev/null @@ -1,15 +0,0 @@ -[DEFAULT] -LastInstrument = 'LARMOR' -SampleHeight = 11 -SampleWidth = 11 -Geometry = Flat Plate -RunOrder = Alternate - TRANS first -NumTrans = 1 -NumSans = 1 -LoopPerRun = False -A2SettingSans = 'Large = 25, 16 mm' -A2SettingTrans = 'Large = 25, 16 mm' -CollectionMode = 'Events' -TableEntries = 'POSITION, TRANS, TRANS_WAIT, SANS, SANS_WAIT, PERIOD, SAMPLE_IDENTIFICATION_STRING, BACKGROUND, THICKNESS, TEMPERATURE, TEMPERATURE2, FIELD, SHEAR_RATE_1, SHEAR_RATE_2, SHEAR_ANGLE_1, SHEAR_ANGLE_2, PRE_COMMAND, POST_COMMAND, RB_NUMBER' -Count_Rate=38 -Move_Time=120 From 416fae993ede6fa82b8baf3e8a67b97952b94706 Mon Sep 17 00:00:00 2001 From: Dirk Honecker Date: Mon, 30 Dec 2024 10:26:15 +0000 Subject: [PATCH 15/88] Fix closing App, print scripts --- App.py | 5 +++++ Dialogs/PreviewDialog.py | 13 ++++++++----- MainWindow.py | 12 ++++++------ Scripting/ScriptGenerator.py | 2 +- 4 files changed, 20 insertions(+), 12 deletions(-) diff --git a/App.py b/App.py index 27e06fe..c0f82eb 100644 --- a/App.py +++ b/App.py @@ -8,10 +8,15 @@ def __init__(self): self.main_window.grid(sticky="nsew") self.root.grid_rowconfigure(1, weight=1) self.root.grid_columnconfigure(1, weight=1) + self.root.protocol("WM_DELETE_WINDOW", self.on_close) + def run(self): self.root.mainloop() + def on_close(self): + self.main_window.on_close() + if __name__ == "__main__": app = App() app.run() \ No newline at end of file diff --git a/Dialogs/PreviewDialog.py b/Dialogs/PreviewDialog.py index 4138fa3..3b03c45 100644 --- a/Dialogs/PreviewDialog.py +++ b/Dialogs/PreviewDialog.py @@ -4,8 +4,10 @@ class PreviewDialog(tk.Toplevel): - def __init__(self, parent): + def __init__(self, parent, script=None): super().__init__(parent) + self.parent = parent # Store reference to the parent (main window) + self.title("PreviewDialog") self.geometry("600x500") @@ -20,8 +22,8 @@ def __init__(self, parent): self.txt_script = scrolledtext.ScrolledText(border_frame, wrap=tk.WORD) self.txt_script.grid(row=0, column=0, sticky="nsew") - self.script_text.insert(tk.END, self.txt_script) - self.script_text.config(state=tk.DISABLED) + self.txt_script.insert(tk.END, script) + self.txt_script.config(state=tk.DISABLED) # Create a frame for the buttons button_frame = tk.Frame(self) button_frame.grid(row=1, column=0, pady=5, sticky="e") @@ -40,8 +42,9 @@ def set_script(self, script): self.txt_script.insert(tk.END, script) def print_script(self): - # Implement the print functionality here - pass + # Call the pnl_settings_on_write_clicked method from the main window + self.parent.pnl_settings_on_write_clicked() + def show(self): self.grab_set() self.wait_window() diff --git a/MainWindow.py b/MainWindow.py index 8af2463..25d20ae 100644 --- a/MainWindow.py +++ b/MainWindow.py @@ -109,13 +109,13 @@ def pnl_settings_on_write_clicked(self): filepath = filedialog.asksaveasfilename(defaultextension=filetypes[0][1], filetypes=filetypes) if filepath: scriptname = filepath.split("/")[-1].replace(" ", "_").rsplit(".", 1)[0] - script_generator= ScriptGenerator() + script_generator= ScriptGenerator(self.instrument_var.get()) script = script_generator.generate_script(self.data.runs, self.settings.experiment, scriptname) with open(filepath, "w") as file: file.write(script) def pnl_settings_on_preview_clicked(self): - script_generator= ScriptGenerator() + script_generator= ScriptGenerator(self.instrument_var.get()) script = script_generator.generate_script(self.data.runs, self.settings.experiment, "my_script") print(script) preview_dialog = PreviewDialog(self, script) @@ -139,7 +139,7 @@ def btn_open_excel_click(self): messagebox.showerror("SANS Script - Error", "Could not open Excel spreadsheet") def mnu_exit_click(self): - self.destroy() + self.on_close() def mnu_export_to_excel_click(self): excel = ExcelIO() @@ -157,8 +157,8 @@ def mnu_save_csv_click(self): def on_close(self): # Save last_instrument to settings settings = Settings() - settings.last_instrument(self.instrument_var) - self.destroy() + settings.save() + self.parent.destroy() @@ -171,5 +171,5 @@ def on_close(self): app.grid(sticky="nsew") root.grid_rowconfigure(0, weight=1) root.grid_columnconfigure(0, weight=1) - #app.protocol("WM_DELETE_WINDOW", app.on_close) + app.protocol("WM_DELETE_WINDOW", app.on_close) app.mainloop() \ No newline at end of file diff --git a/Scripting/ScriptGenerator.py b/Scripting/ScriptGenerator.py index 597628c..16e05fc 100644 --- a/Scripting/ScriptGenerator.py +++ b/Scripting/ScriptGenerator.py @@ -10,7 +10,7 @@ from Models.A2Setting import A2Setting class ScriptGenerator: - def __init__(self, instrument): + def __init__(self, instrument=None): self.indent = " " self.is_instrument = tk.StringVar() # Initialize the attribute self.is_instrument.set(instrument) From 520d3fb403fc06f5553af0f53358d848dd5a53b7 Mon Sep 17 00:00:00 2001 From: Dirk Honecker Date: Mon, 30 Dec 2024 11:03:57 +0000 Subject: [PATCH 16/88] Clean up cs files --- App.config.py | 23 - App.xaml | 8 - App.xaml.cs | 17 - Dialogs/PreviewDialog.xaml | 23 - Dialogs/PreviewDialog.xaml.cs | 40 -- EstimationPanel.xaml | 40 -- EstimationPanel.xaml.cs | 38 -- IO/CsvExporter.cs | 93 --- IO/ExcelIO.cs | 352 ---------- MainWindow.xaml | 62 -- MainWindow.xaml.cs | 197 ------ Models/A2Setting.cs | 24 - Models/DataCollection.cs | 26 - Models/Estimation.cs | 115 ---- Models/Experiment.cs | 290 -------- Models/ExperimentSettings.cs | 292 -------- Models/Geometry.cs | 25 - Models/InstrumentInfo.cs | 98 --- Models/RunOrder.cs | 25 - Models/WaitForUnits.cs | 25 - Properties/AssemblyInfo.cs | 55 -- Properties/Resources.Designer.cs | 63 -- Properties/Resources.Designer.py | 31 - Properties/Resources.ini | 16 - Properties/Resources.resx | 117 ---- Properties/Settings.Designer.cs | 158 ----- Properties/Settings.settings | 39 -- SANS_Script_GUI_v4.csproj | 185 ------ SANS_Script_GUI_v4.csproj.py | 174 ----- SANS_Script_GUI_v4.csproj.user.py | 16 - SANS_Script_GUI_v4.xml | 2 - SANS_Script_Writer/SANS_Script_Writer.sln | 20 - .../SANS_Script_Writer/SANS_Script_Writer.py | 1 - .../SANS_Script_Writer.pyproj | 35 - Scripting/GeniePythonScriptGenerator.cs | 540 --------------- Scripting/IScriptGenerator.cs | 11 - Scripting/OpenGenieScriptGenerator.cs | 627 ------------------ SettingsPanel.xaml | 72 -- SettingsPanel.xaml.cs | 47 -- ViewModels/DataGridVM.cs | 41 -- ViewModels/EstimationPanelVM.cs | 24 - ViewModels/InstrumentVM.cs | 179 ----- ViewModels/InstrumentVM.py | 36 - ViewModels/SettingsPanelVM.cs | 87 --- 44 files changed, 4389 deletions(-) delete mode 100644 App.config.py delete mode 100644 App.xaml delete mode 100644 App.xaml.cs delete mode 100644 Dialogs/PreviewDialog.xaml delete mode 100644 Dialogs/PreviewDialog.xaml.cs delete mode 100644 EstimationPanel.xaml delete mode 100644 EstimationPanel.xaml.cs delete mode 100644 IO/CsvExporter.cs delete mode 100644 IO/ExcelIO.cs delete mode 100644 MainWindow.xaml delete mode 100644 MainWindow.xaml.cs delete mode 100644 Models/A2Setting.cs delete mode 100644 Models/DataCollection.cs delete mode 100644 Models/Estimation.cs delete mode 100644 Models/Experiment.cs delete mode 100644 Models/ExperimentSettings.cs delete mode 100644 Models/Geometry.cs delete mode 100644 Models/InstrumentInfo.cs delete mode 100644 Models/RunOrder.cs delete mode 100644 Models/WaitForUnits.cs delete mode 100644 Properties/AssemblyInfo.cs delete mode 100644 Properties/Resources.Designer.cs delete mode 100644 Properties/Resources.Designer.py delete mode 100644 Properties/Resources.ini delete mode 100644 Properties/Resources.resx delete mode 100644 Properties/Settings.Designer.cs delete mode 100644 Properties/Settings.settings delete mode 100644 SANS_Script_GUI_v4.csproj delete mode 100644 SANS_Script_GUI_v4.csproj.py delete mode 100644 SANS_Script_GUI_v4.csproj.user.py delete mode 100644 SANS_Script_GUI_v4.xml delete mode 100644 SANS_Script_Writer/SANS_Script_Writer.sln delete mode 100644 SANS_Script_Writer/SANS_Script_Writer/SANS_Script_Writer.py delete mode 100644 SANS_Script_Writer/SANS_Script_Writer/SANS_Script_Writer.pyproj delete mode 100644 Scripting/GeniePythonScriptGenerator.cs delete mode 100644 Scripting/IScriptGenerator.cs delete mode 100644 Scripting/OpenGenieScriptGenerator.cs delete mode 100644 SettingsPanel.xaml delete mode 100644 SettingsPanel.xaml.cs delete mode 100644 ViewModels/DataGridVM.cs delete mode 100644 ViewModels/EstimationPanelVM.cs delete mode 100644 ViewModels/InstrumentVM.cs delete mode 100644 ViewModels/InstrumentVM.py delete mode 100644 ViewModels/SettingsPanelVM.cs diff --git a/App.config.py b/App.config.py deleted file mode 100644 index a8c5a2b..0000000 --- a/App.config.py +++ /dev/null @@ -1,23 +0,0 @@ -import configparser - -config = configparser.ConfigParser() - -config['DEFAULT'] = { - 'LastInstrument': 'LARMOR', - 'SampleHeight': '11', - 'SampleWidth': '11', - 'Geometry': 'Flat Plate', - 'RunOrder': 'Alternate - TRANS first', - 'NumTrans': '1', - 'NumSans': '1', - 'LoopPerRun': 'False', - 'A2SettingSans': 'Large = 25, 16 mm', - 'A2SettingTrans': 'Large = 25, 16 mm', - 'CollectionMode': 'Events', - 'TableEntries': 'POSITION, TRANS, TRANS_WAIT, SANS, SANS_WAIT, PERIOD, SAMPLE_IDENTIFICATION_STRING, BACKGROUND, THICKNESS, TEMPERATURE, TEMPERATURE2, FIELD, SHEAR_RATE_1, SHEAR_RATE_2, SHEAR_ANGLE_1, SHEAR_ANGLE_2, PRE_COMMAND, POST_COMMAND, RB_NUMBER', - 'count_rate': '38', - 'move_time': '120' -} - -with open('app_config.ini', 'w') as configfile: - config.write(configfile) \ No newline at end of file diff --git a/App.xaml b/App.xaml deleted file mode 100644 index 704a2c1..0000000 --- a/App.xaml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - diff --git a/App.xaml.cs b/App.xaml.cs deleted file mode 100644 index b99238d..0000000 --- a/App.xaml.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Configuration; -using System.Data; -using System.Linq; -using System.Threading.Tasks; -using System.Windows; - -namespace LOQ_Script_Gui -{ - /// - /// Interaction logic for App.xaml - /// - public partial class App : Application - { - } -} diff --git a/Dialogs/PreviewDialog.xaml b/Dialogs/PreviewDialog.xaml deleted file mode 100644 index b85e3c9..0000000 --- a/Dialogs/PreviewDialog.xaml +++ /dev/null @@ -1,23 +0,0 @@ - - - - - - - - - - - - - - - - - - - - diff --git a/Dialogs/PreviewDialog.xaml.cs b/Dialogs/PreviewDialog.xaml.cs deleted file mode 100644 index 9edb2c7..0000000 --- a/Dialogs/PreviewDialog.xaml.cs +++ /dev/null @@ -1,40 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Windows; -using System.Windows.Controls; -using System.Windows.Data; -using System.Windows.Documents; -using System.Windows.Input; -using System.Windows.Media; -using System.Windows.Media.Imaging; -using System.Windows.Shapes; -using System.IO; -using System.Windows.Xps.Packaging; -using System.Windows.Documents; - -namespace LOQ_Script_Gui.Dialogs -{ - /// - /// Interaction logic for PreviewDialog.xaml - /// - public partial class PreviewDialog : Window - { - public PreviewDialog() - { - InitializeComponent(); - } - - public void SetScript(string script) - { - txtScript.Text = script; - } - - private void Button_Click(object sender, RoutedEventArgs e) - { - this.Close(); - } - } -} diff --git a/EstimationPanel.xaml b/EstimationPanel.xaml deleted file mode 100644 index 214c556..0000000 --- a/EstimationPanel.xaml +++ /dev/null @@ -1,40 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/EstimationPanel.xaml.cs b/EstimationPanel.xaml.cs deleted file mode 100644 index 43a5813..0000000 --- a/EstimationPanel.xaml.cs +++ /dev/null @@ -1,38 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Windows; -using System.Windows.Controls; -using System.Windows.Data; -using System.Windows.Documents; -using System.Windows.Input; -using System.Windows.Media; -using System.Windows.Media.Imaging; -using System.Windows.Navigation; -using System.Windows.Shapes; - -namespace LOQ_Script_Gui -{ - /// - /// Interaction logic for EstimationPanel.xaml - /// - public partial class EstimationPanel : UserControl - { - public event EventHandler OnCalculateClicked; - - public EstimationPanel() - { - InitializeComponent(); - } - - private void btnCalculate_Click(object sender, RoutedEventArgs e) - { - if (OnCalculateClicked != null) - { - OnCalculateClicked(sender, e); - } - } - } -} diff --git a/IO/CsvExporter.cs b/IO/CsvExporter.cs deleted file mode 100644 index d732c1b..0000000 --- a/IO/CsvExporter.cs +++ /dev/null @@ -1,93 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Collections.ObjectModel; -using System.IO; - -namespace LOQ_Script_Gui -{ - class CsvExporter - { - public static void Export(string file, ObservableCollection runs) - { - using(StreamWriter sw = new StreamWriter(file)) - { - foreach(Experiment exp in runs) - { - // Position - WriteParameter(sw, exp.Position); - - // Trans - WriteParameter(sw, exp.Trans.ToString()); - - // Trans wait - WriteParameter(sw, exp.TransWait); - - // Sans - WriteParameter(sw, exp.Sans.ToString()); - - // Sans wait - WriteParameter(sw, exp.SansWait); - - // Period - WriteParameter(sw, exp.Period); - - // Sample ID - WriteParameter(sw, exp.Sample); - - // Sample ID - WriteParameter(sw, exp.Background); - - // Thickness - WriteParameter(sw, exp.Thickness); - - // Temperature 1 - WriteParameter(sw, exp.Temperature1); - - // Temperature 2 - WriteParameter(sw, exp.Temperature2); - - // Field - WriteParameter(sw, exp.Field); - - // Shear rate 1 - WriteParameter(sw, exp.ShearRate1); - - // Shear rate 2 - WriteParameter(sw, exp.ShearRate2); - - // Shear angle 1 - WriteParameter(sw, exp.ShearAngle1); - - // Shear angle 2 - WriteParameter(sw, exp.ShearAngle2); - - // Pre-command - WriteParameter(sw, exp.PreCommand); - - // Post-command - WriteParameter(sw, exp.PostCommand); - - // RB - WriteParameter(sw, exp.RbNumber); - - sw.WriteLine(); - } - } - } - - private static void WriteParameter(StreamWriter sw, string par) - { - if (!string.IsNullOrWhiteSpace(par)) - { - sw.Write(string.Format("{0}, ", par)); - } - else - { - sw.Write(", "); - } - } - } -} diff --git a/IO/ExcelIO.cs b/IO/ExcelIO.cs deleted file mode 100644 index 13ed41f..0000000 --- a/IO/ExcelIO.cs +++ /dev/null @@ -1,352 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Data.OleDb; -using System.Data; -using System.IO; -using System.Reflection; -using System.Collections.ObjectModel; -using System.Windows; - -namespace LOQ_Script_Gui -{ - class ExcelIO - { - static List columns = new List() - { - "POSITION", "TRANS", "TRANS_WAIT_FOR", "SANS", "SANS_WAIT_FOR", "PERIOD", "SAMPLE_IDENTIFICATION_STRING", "BACKGROUND", - "THICKNESS", "TEMPERATURE", "TEMPERATURE2", "FIELD", "SHEAR_RATE_1", "SHEAR_RATE_2", "SHEAR_ANGLE_1", "SHEAR_ANGLE_2", - "PRE_COMMAND", "POST_COMMAND", "RB_NUMBER" - }; - - // The last column used in Excel - static string LastColumn = "S"; - - DataSet dataset = new DataSet(); - - public void OpenExcelWorkBook(string filename) - { - // Create connection string variable - // This is the 32 bit office version requiring Microsoft Access Database Engine 2010 Redistributable - String sConnectionString1 = "Provider=Microsoft.Jet.OLEDB.4.0;" + - "Data Source=" + filename + ";" + - "Extended Properties='Excel 8.0;HDR=Yes;IMEX=1;'"; - // if 64 bit office is installed try this one Microsoft Access Database Engine 2016 Redistributable with _X64 - String sConnectionString2 = "Provider=Microsoft.ACE.OLEDB.12.0;" + - "Data Source=" + filename + ";" + - "Mode=Read;Extended Properties='Excel 12.0;HDR=Yes;IMEX=1;'"; - - // Create connection object - OleDbConnection conn = new OleDbConnection(sConnectionString1); - OleDbConnection conn2 = new OleDbConnection(sConnectionString2); - - try - { - // Open connection - conn.Open(); - - String[] names = GetWorkSheetNames(conn); - // Create new OleDbCommand to return data from the first worksheet - OleDbCommand cmd = new OleDbCommand(@"SELECT * FROM [" + names[0] + "]", conn); - - // Create new OleDbDataAdapter that is used to build a DataSet - // based on the preceding SQL SELECT statement - OleDbDataAdapter adapter = new OleDbDataAdapter(); - - // Pass the Select command to the adapter - adapter.SelectCommand = cmd; - - // Fill the DataSet with the information from the worksheet - adapter.Fill(dataset, "XLData"); - - } - catch (Exception ex) - { - //MessageBox.Show(ex.Message, "SANS Script - Error", MessageBoxButton.OK, MessageBoxImage.Error); - try - { - // Open connection - conn2.Open(); - - String[] names = GetWorkSheetNames(conn2); - // Create new OleDbCommand to return data from the first worksheet - OleDbCommand cmd = new OleDbCommand(@"SELECT * FROM [" + names[0] + "]", conn2); - - // Create new OleDbDataAdapter that is used to build a DataSet - // based on the preceding SQL SELECT statement - OleDbDataAdapter adapter = new OleDbDataAdapter(); - - // Pass the Select command to the adapter - adapter.SelectCommand = cmd; - - // Fill the DataSet with the information from the worksheet - adapter.Fill(dataset, "XLData"); - } - catch (Exception ex1) - { - dataset = null; - MessageBox.Show(ex1.Message, "SANS Script - Error", MessageBoxButton.OK, MessageBoxImage.Error); - throw new IOException("Could not open Excel spreadsheet"); - - } - } - finally - { - // Clean up objects. - conn.Close(); - conn.Dispose(); - conn2.Close(); - conn2.Dispose(); - } - } - - public List ExtractExperiments() - { - List experiments = new List(); - - if (dataset != null) - { - if (dataset.Tables != null && dataset.Tables.Count > 0) - { - if (dataset.Tables[0].Rows != null && dataset.Tables[0].Rows.Count > 0) - { - // Loop through the rows and create an experiment for each - foreach (DataRow dr in dataset.Tables[0].Rows) - { - Experiment exp = new Experiment(); - - // Position - if (!string.IsNullOrWhiteSpace(dr[0].ToString())) - { - exp.Position = dr[0].ToString(); - } - - // Trans - if (!string.IsNullOrWhiteSpace(dr[1].ToString())) - { - exp.Trans = CastToDouble(dr[1].ToString()); - } - - // Trans wait - if (!string.IsNullOrWhiteSpace(dr[2].ToString())) - { - exp.TransWait = dr[2].ToString(); - } - - // Sans - if (!string.IsNullOrWhiteSpace(dr[3].ToString())) - { - exp.Sans = CastToDouble(dr[3].ToString()); - } - - // Sans wait - if (!string.IsNullOrWhiteSpace(dr[4].ToString())) - { - exp.SansWait = dr[4].ToString(); - } - - // Period - if (!string.IsNullOrWhiteSpace(dr[5].ToString())) - { - exp.Period = dr[5].ToString(); - } - - // Sample ID - if (!string.IsNullOrWhiteSpace(dr[6].ToString())) - { - exp.Sample = dr[6].ToString(); - } - - // Background - if (!string.IsNullOrWhiteSpace(dr[7].ToString())) - { - exp.Background = dr[7].ToString(); - } - - // Thickness - if (!string.IsNullOrWhiteSpace(dr[8].ToString())) - { - exp.Thickness = dr[8].ToString(); - } - - // Temperature 1 - if (!string.IsNullOrWhiteSpace(dr[9].ToString())) - { - exp.Temperature1 = dr[9].ToString(); - } - - // Temperature 2 - if (!string.IsNullOrWhiteSpace(dr[10].ToString())) - { - exp.Temperature2 = dr[10].ToString(); - } - - // Field - if (!string.IsNullOrWhiteSpace(dr[11].ToString())) - { - exp.Field = dr[11].ToString(); - } - - // Shear rate 1 - if (!string.IsNullOrWhiteSpace(dr[12].ToString())) - { - exp.ShearRate1 = dr[12].ToString(); - } - - // Shear rate 2 - if (!string.IsNullOrWhiteSpace(dr[13].ToString())) - { - exp.ShearRate2 = dr[13].ToString(); - } - - // Shear angle 1 - if (!string.IsNullOrWhiteSpace(dr[14].ToString())) - { - exp.ShearAngle1 = dr[14].ToString(); - } - - // Shear angle 2 - if (!string.IsNullOrWhiteSpace(dr[15].ToString())) - { - exp.ShearAngle2 = dr[15].ToString(); - } - - // Pre-command - if (!string.IsNullOrWhiteSpace(dr[16].ToString())) - { - exp.PreCommand = dr[16].ToString(); - } - - // Post-command - if (!string.IsNullOrWhiteSpace(dr[17].ToString())) - { - exp.PostCommand = dr[17].ToString(); - } - - // RB - if (!string.IsNullOrWhiteSpace(dr[18].ToString())) - { - exp.RbNumber = dr[18].ToString(); - } - - experiments.Add(exp); - } - - } - } - } - - return experiments; - } - - private static double CastToDouble(string val) - { - double result; - if (Double.TryParse(val, out result)) - { - return result; - } - - return 0.0; - } - - private static String[] GetWorkSheetNames(OleDbConnection conn) - { - System.Data.DataTable dt = null; - - try - { - // Get the data table containg the schema guid - dt = conn.GetOleDbSchemaTable(OleDbSchemaGuid.Tables, null); - - if (dt == null) - { - return null; - } - - String[] excelSheets = new String[dt.Rows.Count]; - int i = 0; - - // Add the sheet name to the string array - foreach (DataRow row in dt.Rows) - { - excelSheets[i] = row["TABLE_NAME"].ToString(); - i++; - } - - return excelSheets; - } - catch (Exception ex) - { - throw (ex); - } - finally - { - if (dt != null) - { - dt.Dispose(); - } - } - } - - public void ExportDataToExcel(ObservableCollection runs) - { - Microsoft.Office.Interop.Excel.Application xlApp; - Microsoft.Office.Interop.Excel.Workbook xlBook; - Microsoft.Office.Interop.Excel.Worksheet xlSheet; - Microsoft.Office.Interop.Excel.Range xlRange; - - try - { - // Start Excel - xlApp = new Microsoft.Office.Interop.Excel.Application(); - xlApp.Visible = true; - - // Create a workbook - xlBook = xlApp.Workbooks.Add(Missing.Value); - - // Set to page - xlSheet = (Microsoft.Office.Interop.Excel.Worksheet)xlBook.ActiveSheet; - - // Create headings - for (int i = 0; i < columns.Count; ++i) - { - xlSheet.Cells[1, i + 1] = columns[i]; - } - - xlSheet.get_Range("A1", LastColumn + "1").Font.Bold = true; - - // Enter data - for (int i = 0; i < runs.Count; ++i) - { - Experiment exp = runs[i]; - List data = new List() - { - exp.Position, exp.Trans.ToString(), exp.TransWait, exp.Sans.ToString(), exp.SansWait, - exp.Period, exp.Sample, exp.Background, exp.Thickness, exp.Temperature1, exp.Temperature2, exp.Field, - exp.ShearRate1, exp.ShearRate2, exp.ShearAngle1, exp.ShearAngle2, exp.PreCommand, - exp.PostCommand, exp.RbNumber - }; - - int row = i + 2; - var range = xlSheet.get_Range("A" + row, LastColumn + row); - range.Value = data.ToArray(); - } - - //Autofit columns - xlRange = xlSheet.get_Range("A1", LastColumn + "1"); - xlRange.EntireColumn.AutoFit(); - - //Detach - xlApp.UserControl = true; - - } - catch (Exception err) - { - throw err; - } - } - } -} diff --git a/MainWindow.xaml b/MainWindow.xaml deleted file mode 100644 index bb398d7..0000000 --- a/MainWindow.xaml +++ /dev/null @@ -1,62 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/MainWindow.xaml.cs b/MainWindow.xaml.cs deleted file mode 100644 index 03ba0f6..0000000 --- a/MainWindow.xaml.cs +++ /dev/null @@ -1,197 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Windows; -using System.Windows.Controls; -using System.Windows.Data; -using System.Windows.Documents; -using System.Windows.Input; -using System.Windows.Media; -using System.Windows.Media.Imaging; -using System.Windows.Navigation; -using System.Windows.Shapes; -using System.Collections.ObjectModel; -using System.IO; - -namespace LOQ_Script_Gui -{ - public partial class MainWindow : Window - { - DataGridVM data = new DataGridVM(); - SettingsPanelVM settings; - Estimation estimate = new Estimation(); - InstrumentInfo instInfo; - InstrumentVM instrument; - - public MainWindow() - { - InitializeComponent(); - - instInfo = new InstrumentInfo(); - settings = new SettingsPanelVM(instInfo); - - dataGrid.DataContext = data; - - pnlSettings.DataContext = settings; - pnlSettings.OnPreviewClicked += pnlSettings_OnPreviewClicked; - pnlSettings.OnWriteClicked += pnlSettings_OnWriteClicked; - - pnlEstimate.DataContext = estimate; - pnlEstimate.OnCalculateClicked += pnlEstimate_OnCalculateClicked; - - instrument = new InstrumentVM(settings.Experiment, estimate); - mnuMain.DataContext = instrument; - - string inst = Properties.Settings.Default.LastInstrument; - switch (inst) - { - case "LARMOR": - instrument.IsLarmor = true; - break; - case "LOQ": - instrument.IsLoq = true; - break; - case "SANS2D": - instrument.IsSans2d = true; - break; - case "ZOOM": - instrument.IsZoom = true; - break; - default: - instrument.IsLarmor = true; - break; - } - } - - void pnlSettings_OnWriteClicked(object sender, EventArgs e) - { - Microsoft.Win32.SaveFileDialog dlg = new Microsoft.Win32.SaveFileDialog(); - - if (instrument.IsLarmor || instrument.IsLoq || instrument.IsZoom || instrument.IsSans2d) - { - dlg.DefaultExt = ".py"; - dlg.Filter = "genie_python Files|*.py"; - } - else - { - dlg.DefaultExt = ".gcl"; - dlg.Filter = "Open Genie Files|*.gcl"; - } - - if ((bool)dlg.ShowDialog()) - { - // Use the filename provided to set the name of the script, this will be ignored by the opengenie generator - string[] filepath = dlg.FileName.Split('\\'); - int lenarr = filepath.Count(); - string scriptname = filepath[lenarr - 1]; - lenarr = scriptname.Length; - scriptname = scriptname.Substring(0, lenarr - 3); - // check for spaces and replace with underscores. - scriptname = scriptname.Replace(" ", "_"); - - string script = InstrumentInfo.ScriptGenerator.GenerateScript(data.Runs, settings.Experiment, scriptname); - - using (StreamWriter sw = new StreamWriter(dlg.FileName)) - { - sw.Write(script); - } - } - } - - void pnlSettings_OnPreviewClicked(object sender, EventArgs e) - { - string script = InstrumentInfo.ScriptGenerator.GenerateScript(data.Runs, settings.Experiment,"my_script"); - Dialogs.PreviewDialog preview = new Dialogs.PreviewDialog(); - preview.SetScript(script); - - preview.ShowDialog(); - } - - void pnlEstimate_OnCalculateClicked(object sender, EventArgs e) - { - estimate.ScriptTime = InstrumentInfo.ScriptGenerator.EstimateRunTime(data.Runs, settings.Experiment, estimate); - } - - private void btnCreateNewTable_Click(object sender, RoutedEventArgs e) - { - data.Runs.Clear(); - } - - private void btnOpenExcel_Click(object sender, RoutedEventArgs e) - { - Microsoft.Win32.OpenFileDialog dlg = new Microsoft.Win32.OpenFileDialog(); - dlg.DefaultExt = ".xlsx"; - dlg.Filter = "Excel Files|*.xls;*.xlsx"; - dlg.Multiselect = false; - - if ((bool)dlg.ShowDialog()) - { - try - { - ExcelIO excel = new ExcelIO(); - excel.OpenExcelWorkBook(dlg.FileName); - List exps = excel.ExtractExperiments(); - data.Runs = new ObservableCollection(exps); - } - catch (Exception err) - { - MessageBox.Show("Could not open Excel spreadsheet", "SANS Script - Error", MessageBoxButton.OK, MessageBoxImage.Error); - } - } - } - - private void mnuExit_Click(object sender, RoutedEventArgs e) - { - Close(); - } - - private void mnuExportToExcel_Click(object sender, RoutedEventArgs e) - { - ExcelIO excel = new ExcelIO(); - excel.ExportDataToExcel(data.Runs); - } - - private void mnuSaveCsv_Click(object sender, RoutedEventArgs e) - { - Microsoft.Win32.SaveFileDialog dlg = new Microsoft.Win32.SaveFileDialog(); - dlg.DefaultExt = ".csv"; - dlg.Filter = "CSV File|*.csv"; - - if ((bool)dlg.ShowDialog()) - { - try - { - CsvExporter.Export(dlg.FileName, data.Runs); - } - catch (Exception err) - { - MessageBox.Show("Could not save as CSV", "SANS Script - Error", MessageBoxButton.OK, MessageBoxImage.Error); - } - } - } - - private void Window_Closed(object sender, EventArgs e) - { - if (instrument.IsLarmor) - { - Properties.Settings.Default.LastInstrument = "LARMOR"; - } - else if (instrument.IsLoq) - { - Properties.Settings.Default.LastInstrument = "LOQ"; - } - else if (instrument.IsSans2d) - { - Properties.Settings.Default.LastInstrument = "SANS2D"; - } - else if (instrument.IsZoom) - { - Properties.Settings.Default.LastInstrument = "ZOOM"; - } - - Properties.Settings.Default.Save(); - } - } -} diff --git a/Models/A2Setting.cs b/Models/A2Setting.cs deleted file mode 100644 index 3111874..0000000 --- a/Models/A2Setting.cs +++ /dev/null @@ -1,24 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Collections.ObjectModel; -using System.ComponentModel; - -namespace LOQ_Script_Gui -{ - class A2Setting - { - public readonly static string XLarge = "XLarge = 20x18 mm"; - public readonly static string Large = "Large = 17 mm"; - public readonly static string Medium = "Medium = 12 mm"; - public readonly static string Small = "Small = 5/1 mm"; - public readonly static string Trans = "Trans = 1 mm"; - - public readonly static string Larmor = ""; - //public readonly static string LarmorLarge = "Large = 25, 16 mm"; - //public readonly static string LarmorMedium = "Medium = 20, 14 mm"; - //public readonly static string LarmorSmall = "Small = 15, 10 mm"; - } -} diff --git a/Models/DataCollection.cs b/Models/DataCollection.cs deleted file mode 100644 index 72fb51b..0000000 --- a/Models/DataCollection.cs +++ /dev/null @@ -1,26 +0,0 @@ -using System.Collections.ObjectModel; - -namespace LOQ_Script_Gui -{ - class DataCollection - { - public readonly static string Histogram = "Histogram"; - public readonly static string Events = "Events"; - public readonly static string SESANS = "SESANS"; - public readonly static string TRANSSANS_Events = "TRANSSANS_Events"; - public readonly static string TRANSSANS_Histogram = "TRANSSANS_Histogram"; - - static ObservableCollection choices = new ObservableCollection() - { - Histogram, TRANSSANS_Histogram, Events, TRANSSANS_Events, SESANS - }; - - public static ObservableCollection Choices - { - get - { - return choices; - } - } - } -} diff --git a/Models/Estimation.cs b/Models/Estimation.cs deleted file mode 100644 index 459a634..0000000 --- a/Models/Estimation.cs +++ /dev/null @@ -1,115 +0,0 @@ -using System.ComponentModel; -using System; - -namespace LOQ_Script_Gui -{ - class Estimation : INotifyPropertyChanged, IDataErrorInfo - { - public event PropertyChangedEventHandler PropertyChanged; - - protected void OnPropertyChanged(string name) - { - PropertyChangedEventHandler handler = PropertyChanged; - if (handler != null) - { - handler(this, new PropertyChangedEventArgs(name)); - } - } - - private double countRate = 40; - public double CountRate - { - get - { - return countRate; - } - set - { - countRate = value; - OnPropertyChanged("CountRate"); - } - } - - private double moveTime = 120; - public double MoveTime - { - get - { - return moveTime; - } - set - { - moveTime = value; - OnPropertyChanged("MoveTime"); - } - } - - private string scriptTime; - public string ScriptTime - { - get - { - return scriptTime; - } - set - { - scriptTime = value; - OnPropertyChanged("ScriptTime"); - } - } - - private bool isValid; - public bool IsValid - { - get - { - return isValid; - } - set - { - isValid = value; - OnPropertyChanged("IsValid"); - } - } - - public string Error - { - get { return String.Empty; } - } - - public string this[string columnName] - { - get - { - string errorMessage = String.Empty; - - switch (columnName) - { - case "CountRate": - if (CountRate < 0) - { - errorMessage = "Count rate cannot be negative!"; - } - break; - case "MoveTime": - if (MoveTime < 0) - { - errorMessage = "Move time cannot be negative!"; - } - break; - } - - if (string.IsNullOrWhiteSpace(errorMessage)) - { - IsValid = true; - } - else - { - IsValid = false; - } - - return errorMessage; - } - } - } -} diff --git a/Models/Experiment.cs b/Models/Experiment.cs deleted file mode 100644 index d88a6ba..0000000 --- a/Models/Experiment.cs +++ /dev/null @@ -1,290 +0,0 @@ -using System.ComponentModel; - -namespace LOQ_Script_Gui -{ - class Experiment : INotifyPropertyChanged - { - public event PropertyChangedEventHandler PropertyChanged; - - protected void OnPropertyChanged(string name) - { - PropertyChangedEventHandler handler = PropertyChanged; - if (handler != null) - { - handler(this, new PropertyChangedEventArgs(name)); - } - } - - private string position; - public string Position - { - get - { - return position; - } - set - { - position = value; - OnPropertyChanged("Position"); - } - } - - private double trans; - public double Trans - { - get - { - return trans; - } - set - { - trans = value; - OnPropertyChanged("Trans"); - } - } - - private string transWait; - public string TransWait - { - get - { - return transWait; - } - set - { - transWait = value; - OnPropertyChanged("TransWait"); - } - } - - private double sans; - public double Sans - { - get - { - return sans; - } - set - { - sans = value; - OnPropertyChanged("Sans"); - } - } - - private string sansWait; - public string SansWait - { - get - { - return sansWait; - } - set - { - sansWait = value; - OnPropertyChanged("SansWait"); - } - } - - private string period; - public string Period - { - get - { - return period; - } - set - { - period = value; - OnPropertyChanged("Period"); - } - } - - private string background; - public string Background - { - get - { - return background; - } - set - { - background = value; - OnPropertyChanged("Background"); - } - } - - private string sample; - public string Sample - { - get - { - return sample; - } - set - { - sample = value; - OnPropertyChanged("Sample"); - } - } - - private string thickness; - public string Thickness - { - get - { - return thickness; - } - set - { - thickness = value; - OnPropertyChanged("Thickness"); - } - } - - private string temperature1; - public string Temperature1 - { - get - { - return temperature1; - } - set - { - temperature1 = value; - OnPropertyChanged("Temperature1"); - } - } - - private string temperature2; - public string Temperature2 - { - get - { - return temperature2; - } - set - { - temperature2 = value; - OnPropertyChanged("Temperature2"); - } - } - - private string field; - public string Field - { - get - { - return field; - } - set - { - field = value; - OnPropertyChanged("Field"); - } - } - - private string shearRate1; - public string ShearRate1 - { - get - { - return shearRate1; - } - set - { - shearRate1 = value; - OnPropertyChanged("ShearRate1"); - } - } - - private string shearRate2; - public string ShearRate2 - { - get - { - return shearRate2; - } - set - { - shearRate2 = value; - OnPropertyChanged("ShearRate2"); - } - } - - private string shearAngle1; - public string ShearAngle1 - { - get - { - return shearAngle1; - } - set - { - shearAngle1 = value; - OnPropertyChanged("ShearAngle1"); - } - } - - private string shearAngle2; - public string ShearAngle2 - { - get - { - return shearAngle2; - } - set - { - shearAngle2 = value; - OnPropertyChanged("ShearAngle2"); - } - } - - private string preCommand; - public string PreCommand - { - get - { - return preCommand; - } - set - { - preCommand = value; - OnPropertyChanged("PreCommand"); - } - } - - private string postCommand; - public string PostCommand - { - get - { - return postCommand; - } - set - { - postCommand = value; - OnPropertyChanged("PostCommand"); - } - } - - private string rbNumber; - public string RbNumber - { - get - { - return rbNumber; - } - set - { - rbNumber = value; - OnPropertyChanged("RbNumber"); - } - } - - public Experiment() - { - sans = 0; - trans = 0; - } - } -} diff --git a/Models/ExperimentSettings.cs b/Models/ExperimentSettings.cs deleted file mode 100644 index dee32a1..0000000 --- a/Models/ExperimentSettings.cs +++ /dev/null @@ -1,292 +0,0 @@ -using System; -using System.ComponentModel; - -namespace LOQ_Script_Gui -{ - class ExperimentSettings : INotifyPropertyChanged, IDataErrorInfo - { - public event PropertyChangedEventHandler PropertyChanged; - - protected void OnPropertyChanged(string name) - { - PropertyChangedEventHandler handler = PropertyChanged; - if (handler != null) - { - handler(this, new PropertyChangedEventArgs(name)); - } - } - - //private string order = RunOrder.TransFirst; - private string order = Properties.Settings.Default.RunOrder; - - public string Order - { - get - { - return order; - } - set - { - order = value; - OnPropertyChanged("Order"); - Properties.Settings.Default.RunOrder = order; - Properties.Settings.Default.Save(); - } - } - - //private int numSans = 1; - private int numSans = Convert.ToInt16(Properties.Settings.Default.NumSans); - public int NumSans - { - get - { - return numSans; - } - set - { - numSans = value; - OnPropertyChanged("NumSans"); - Properties.Settings.Default.NumSans = Convert.ToString(numSans); - Properties.Settings.Default.Save(); - } - } - - //private int numTrans = 1; - private int numTrans = Convert.ToInt16(Properties.Settings.Default.NumTrans); - public int NumTrans - { - get - { - return numTrans; - } - set - { - numTrans = value; - OnPropertyChanged("NumTrans"); - Properties.Settings.Default.NumTrans = Convert.ToString(numTrans); - Properties.Settings.Default.Save(); - } - } - - //private bool loopPerRun; - private bool loopPerRun = Convert.ToBoolean(Properties.Settings.Default.LoopPerRun); - - public bool LoopPerRun - { - get - { - return loopPerRun; - } - set - { - loopPerRun = value; - OnPropertyChanged("LoopPerRun"); - Properties.Settings.Default.LoopPerRun = Convert.ToString(LoopPerRun); - Properties.Settings.Default.Save(); - } - } - - //private string a2SettingSans = A2Setting.Large; - private string a2SettingSans = Properties.Settings.Default.A2SettingSans; - public string A2SettingSans - { - get - { - return a2SettingSans; - } - set - { - a2SettingSans = value; - OnPropertyChanged("A2SettingSans"); - Properties.Settings.Default.A2SettingSans = a2SettingSans; - Properties.Settings.Default.Save(); - } - } - - //private string a2SettingTrans = A2Setting.Large; - private string a2SettingTrans = Properties.Settings.Default.A2SettingTrans; - public string A2SettingTrans - { - get - { - return a2SettingTrans; - } - set - { - a2SettingTrans = value; - OnPropertyChanged("A2SettingTrans"); - Properties.Settings.Default.A2SettingTrans = a2SettingTrans; - Properties.Settings.Default.Save(); - } - } - - //private string sampleGeometry = Geometry.FlatPlate; - private string sampleGeometry = Properties.Settings.Default.Geometry; - public string SampleGeometry - { - get - { - return sampleGeometry; - } - set - { - sampleGeometry = value; - - if (sampleGeometry == Geometry.Disc) - { - SampleWidthEnabled = false; - } - else - { - SampleWidthEnabled = true; - } - - OnPropertyChanged("SampleGeometry"); - Properties.Settings.Default.Geometry = sampleGeometry; - Properties.Settings.Default.Save(); - } - } - - //private double sampleHeight = 11; - private double sampleHeight = Convert.ToDouble(Properties.Settings.Default.SampleHeight); - public double SampleHeight - { - get - { - return sampleHeight; - } - set - { - sampleHeight = value; - if (!sampleWidthEnabled) - { - // Width takes the same value as height - SampleWidth = value; - } - - OnPropertyChanged("SampleHeight"); - Properties.Settings.Default.SampleHeight = Convert.ToString(sampleHeight); - Properties.Settings.Default.Save(); - } - } - - //private double sampleWidth = 11; - private double sampleWidth = Convert.ToDouble(Properties.Settings.Default.SampleWidth); - public double SampleWidth - { - get - { - return sampleWidth; - } - set - { - sampleWidth = value; - OnPropertyChanged("SampleWidth"); - Properties.Settings.Default.SampleWidth = Convert.ToString(sampleWidth); - Properties.Settings.Default.Save(); - } - } - - private bool sampleWidthEnabled = true; - public bool SampleWidthEnabled - { - get - { - return sampleWidthEnabled; - } - set - { - sampleWidthEnabled = value; - if (!value) - { - // Width takes the same value as height - SampleWidth = SampleHeight; - } - - OnPropertyChanged("SampleWidthEnabled"); - } - } - - //private string collectionMode = DataCollection.Events; - private string collectionMode = Properties.Settings.Default.CollectionMode; - public string CollectionMode - { - get - { - return collectionMode; - } - set - { - collectionMode = value; - OnPropertyChanged("CollectionMode"); - Properties.Settings.Default.CollectionMode = collectionMode; - Properties.Settings.Default.Save(); - } - } - - private bool isValid = true; - public bool IsValid - { - get - { - return isValid; - } - set - { - isValid = value; - OnPropertyChanged("IsValid"); - } - } - - public string Error - { - get { return String.Empty; } - } - - public string this[string columnName] - { - get - { - string errorMessage = String.Empty; - - switch (columnName) - { - case "NumSans": - if (NumSans < 0) - { - errorMessage = "Number of SANS runs cannot be negative!"; - } - break; - case "NumTrans": - if (NumTrans < 0) - { - errorMessage = "Number of TRANS runs cannot be negative!"; - } - break; - case "SampleHeight": - if (SampleHeight <= 0 || SampleHeight > 20) - { - errorMessage = "Sample height must be between 0 and 20"; - } - break; - case "SampleWidth": - if (SampleWidth <= 0 || SampleWidth > 20) - { - errorMessage = "Sample width must be between 0 and 20"; - } - break; - } - - if (string.IsNullOrWhiteSpace(errorMessage)) - { - IsValid = true; - } - else - { - IsValid = false; - } - return errorMessage; - } - } - } -} diff --git a/Models/Geometry.cs b/Models/Geometry.cs deleted file mode 100644 index d20abc8..0000000 --- a/Models/Geometry.cs +++ /dev/null @@ -1,25 +0,0 @@ -using System.Collections.ObjectModel; - -namespace LOQ_Script_Gui -{ - class Geometry - { - public readonly static string Disc = "Disc"; - public readonly static string Cylindrical = "Cylindrical"; - public readonly static string FlatPlate = "Flat Plate"; - public readonly static string SingleCrystal = "Single Crystal"; - - static ObservableCollection choices = new ObservableCollection() - { - Disc, Cylindrical, FlatPlate, SingleCrystal - }; - - public static ObservableCollection Choices - { - get - { - return choices; - } - } - } -} diff --git a/Models/InstrumentInfo.cs b/Models/InstrumentInfo.cs deleted file mode 100644 index 00ce2fc..0000000 --- a/Models/InstrumentInfo.cs +++ /dev/null @@ -1,98 +0,0 @@ -using System; -using System.Collections.Generic; -using System.ComponentModel; -using LOQ_Script_Gui.Scripting; -using System.Collections.ObjectModel; - -namespace LOQ_Script_Gui -{ - /// - /// Contains the information needed to set the GUI up for the specified instrument. - /// - class InstrumentInfo : INotifyPropertyChanged - { - public event PropertyChangedEventHandler PropertyChanged; - - protected void OnPropertyChanged(string name) - { - PropertyChangedEventHandler handler = PropertyChanged; - if (handler != null) - { - handler(this, new PropertyChangedEventArgs(name)); - } - } - - public static event EventHandler StaticPropertyChanged = delegate { }; - - protected static void OnStaticPropertyChanged(string propertyName) - { - if (StaticPropertyChanged != null) - StaticPropertyChanged(null, new PropertyChangedEventArgs(propertyName)); - } - - static string a2Label = "A2 Setting:"; - public static string A2Label - { - get - { - return a2Label; - } - set - { - a2Label = value; - OnStaticPropertyChanged("A2Label"); - } - } - - // Provides the combo box options - // These values can change depending on the instrument - static ObservableCollection a2Choices = new ObservableCollection() - { - A2Setting.Large, A2Setting.Medium, A2Setting.Small - }; - - public static ObservableCollection A2Choices - { - get - { - return a2Choices; - } - - set - { - a2Choices = value; - OnStaticPropertyChanged("A2Choices"); - } - } - - static bool a2TransEnabled = true; - public static bool A2TransEnabled - { - get - { - return a2TransEnabled; - } - set - { - a2TransEnabled = value; - OnStaticPropertyChanged("A2TransEnabled"); - } - } - - static bool collectionModeEnabled = false; - public static bool CollectionModeEnabled - { - get - { - return collectionModeEnabled; - } - set - { - collectionModeEnabled = value; - OnStaticPropertyChanged("CollectionModeEnabled"); - } - } - - public static IScriptGenerator ScriptGenerator { get; set; } - } -} diff --git a/Models/RunOrder.cs b/Models/RunOrder.cs deleted file mode 100644 index 8aba130..0000000 --- a/Models/RunOrder.cs +++ /dev/null @@ -1,25 +0,0 @@ -using System.Collections.ObjectModel; - -namespace LOQ_Script_Gui -{ - class RunOrder - { - public readonly static string AllTrans = "All TRANS first"; - public readonly static string TransFirst = "Alternate - TRANS first"; - public readonly static string AllSans = "All SANS first"; - public readonly static string SansFirst = "Alternate - SANS first"; - - static ObservableCollection choices = new ObservableCollection() - { - AllTrans, TransFirst, AllSans, SansFirst - }; - - public static ObservableCollection Choices - { - get - { - return choices; - } - } - } -} diff --git a/Models/WaitForUnits.cs b/Models/WaitForUnits.cs deleted file mode 100644 index 0484abf..0000000 --- a/Models/WaitForUnits.cs +++ /dev/null @@ -1,25 +0,0 @@ -using System.Collections.ObjectModel; - -namespace LOQ_Script_Gui -{ - class WaitForUnits - { - public readonly static string MicroAmps = "Microamp-hours"; - public readonly static string Minutes = "Minutes"; - public readonly static string Seconds = "Seconds"; - public readonly static string Frames = "Frames"; - - static ObservableCollection choices = new ObservableCollection() - { - MicroAmps, Minutes, Seconds, Frames - }; - - public static ObservableCollection Choices - { - get - { - return choices; - } - } - } -} diff --git a/Properties/AssemblyInfo.cs b/Properties/AssemblyInfo.cs deleted file mode 100644 index 1f2339a..0000000 --- a/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,55 +0,0 @@ -using System.Reflection; -using System.Resources; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; -using System.Windows; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("LOQ_Script_Gui")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("LOQ_Script_Gui")] -[assembly: AssemblyCopyright("Copyright © 2015")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. -[assembly: ComVisible(false)] - -//In order to begin building localizable applications, set -//CultureYouAreCodingWith in your .csproj file -//inside a . For example, if you are using US english -//in your source files, set the to en-US. Then uncomment -//the NeutralResourceLanguage attribute below. Update the "en-US" in -//the line below to match the UICulture setting in the project file. - -//[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)] - - -[assembly: ThemeInfo( - ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located - //(used if a resource is not found in the page, - // or application resource dictionaries) - ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located - //(used if a resource is not found in the page, - // app, or any theme specific resource dictionaries) -)] - - -// Version information for an assembly consists of the following four values: -// -// Major Version -// Minor Version -// Build Number -// Revision -// -// You can specify all the values or you can default the Build and Revision Numbers -// by using the '*' as shown below: -// [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.0.0.0")] -[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/Properties/Resources.Designer.cs b/Properties/Resources.Designer.cs deleted file mode 100644 index 2b67d17..0000000 --- a/Properties/Resources.Designer.cs +++ /dev/null @@ -1,63 +0,0 @@ -//------------------------------------------------------------------------------ -// -// This code was generated by a tool. -// Runtime Version:4.0.30319.42000 -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -//------------------------------------------------------------------------------ - -namespace LOQ_Script_Gui.Properties { - using System; - - - /// - /// A strongly-typed resource class, for looking up localized strings, etc. - /// - // This class was auto-generated by the StronglyTypedResourceBuilder - // class via a tool like ResGen or Visual Studio. - // To add or remove a member, edit your .ResX file then rerun ResGen - // with the /str option, or rebuild your VS project. - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - internal class Resources { - - private static global::System.Resources.ResourceManager resourceMan; - - private static global::System.Globalization.CultureInfo resourceCulture; - - [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] - internal Resources() { - } - - /// - /// Returns the cached ResourceManager instance used by this class. - /// - [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - internal static global::System.Resources.ResourceManager ResourceManager { - get { - if (object.ReferenceEquals(resourceMan, null)) { - global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("LOQ_Script_Gui.Properties.Resources", typeof(Resources).Assembly); - resourceMan = temp; - } - return resourceMan; - } - } - - /// - /// Overrides the current thread's CurrentUICulture property for all - /// resource lookups using this strongly typed resource class. - /// - [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - internal static global::System.Globalization.CultureInfo Culture { - get { - return resourceCulture; - } - set { - resourceCulture = value; - } - } - } -} diff --git a/Properties/Resources.Designer.py b/Properties/Resources.Designer.py deleted file mode 100644 index d4614f7..0000000 --- a/Properties/Resources.Designer.py +++ /dev/null @@ -1,31 +0,0 @@ -import gettext -import os - -class Resources: - def __init__(self): - self.resource_man = None - self.resource_culture = None - - @property - def resource_manager(self): - if self.resource_man is None: - locale_dir = os.path.join(os.path.dirname(__file__), 'locale') - self.resource_man = gettext.translation('resources', localedir=locale_dir, languages=[self.resource_culture]) - return self.resource_man - - @property - def culture(self): - return self.resource_culture - - @culture.setter - def culture(self, value): - self.resource_culture = value - self.resource_man = None # Reset the resource manager to reload with the new culture - -# Example usage -resources = Resources() -resources.culture = 'en_US' # Set the desired culture -_ = resources.resource_manager.gettext - -# Example of using the localized string -print(_("Hello, World!")) \ No newline at end of file diff --git a/Properties/Resources.ini b/Properties/Resources.ini deleted file mode 100644 index 0d87d37..0000000 --- a/Properties/Resources.ini +++ /dev/null @@ -1,16 +0,0 @@ -#using configparser -[resheader] -resmimetype = text/microsoft-resx -version = 2.0 -reader = System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 -writer = System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - -[data] -Name1 = this is my long string -Color1 = Blue -Bitmap1 = [base64 mime encoded serialized .NET Framework object] -Icon1 = [base64 mime encoded string representing a byte array form of the .NET Framework object] - -[metadata] -Name1.comment = this is a comment -Icon1.comment = This is a comment \ No newline at end of file diff --git a/Properties/Resources.resx b/Properties/Resources.resx deleted file mode 100644 index af7dbeb..0000000 --- a/Properties/Resources.resx +++ /dev/null @@ -1,117 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - \ No newline at end of file diff --git a/Properties/Settings.Designer.cs b/Properties/Settings.Designer.cs deleted file mode 100644 index 5f01f51..0000000 --- a/Properties/Settings.Designer.cs +++ /dev/null @@ -1,158 +0,0 @@ -//------------------------------------------------------------------------------ -// -// This code was generated by a tool. -// Runtime Version:4.0.30319.42000 -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -//------------------------------------------------------------------------------ - -namespace LOQ_Script_Gui.Properties { - - - [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "17.0.3.0")] - internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { - - private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); - - public static Settings Default { - get { - return defaultInstance; - } - } - - [global::System.Configuration.UserScopedSettingAttribute()] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Configuration.DefaultSettingValueAttribute("LARMOR")] - public string LastInstrument { - get { - return ((string)(this["LastInstrument"])); - } - set { - this["LastInstrument"] = value; - } - } - - [global::System.Configuration.UserScopedSettingAttribute()] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Configuration.DefaultSettingValueAttribute("11")] - public string SampleHeight { - get { - return ((string)(this["SampleHeight"])); - } - set { - this["SampleHeight"] = value; - } - } - - [global::System.Configuration.UserScopedSettingAttribute()] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Configuration.DefaultSettingValueAttribute("11")] - public string SampleWidth { - get { - return ((string)(this["SampleWidth"])); - } - set { - this["SampleWidth"] = value; - } - } - - [global::System.Configuration.UserScopedSettingAttribute()] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Configuration.DefaultSettingValueAttribute("Flat Plate")] - public string Geometry { - get { - return ((string)(this["Geometry"])); - } - set { - this["Geometry"] = value; - } - } - - [global::System.Configuration.UserScopedSettingAttribute()] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Configuration.DefaultSettingValueAttribute("Alternate - TRANS first")] - public string RunOrder { - get { - return ((string)(this["RunOrder"])); - } - set { - this["RunOrder"] = value; - } - } - - [global::System.Configuration.UserScopedSettingAttribute()] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Configuration.DefaultSettingValueAttribute("1")] - public string NumTrans { - get { - return ((string)(this["NumTrans"])); - } - set { - this["NumTrans"] = value; - } - } - - [global::System.Configuration.UserScopedSettingAttribute()] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Configuration.DefaultSettingValueAttribute("1")] - public string NumSans { - get { - return ((string)(this["NumSans"])); - } - set { - this["NumSans"] = value; - } - } - - [global::System.Configuration.UserScopedSettingAttribute()] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Configuration.DefaultSettingValueAttribute("False")] - public string LoopPerRun { - get { - return ((string)(this["LoopPerRun"])); - } - set { - this["LoopPerRun"] = value; - } - } - - [global::System.Configuration.UserScopedSettingAttribute()] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Configuration.DefaultSettingValueAttribute("Large = 25, 16 mm")] - public string A2SettingSans { - get { - return ((string)(this["A2SettingSans"])); - } - set { - this["A2SettingSans"] = value; - } - } - - [global::System.Configuration.UserScopedSettingAttribute()] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Configuration.DefaultSettingValueAttribute("Large = 25, 16 mm")] - public string A2SettingTrans { - get { - return ((string)(this["A2SettingTrans"])); - } - set { - this["A2SettingTrans"] = value; - } - } - - [global::System.Configuration.UserScopedSettingAttribute()] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Configuration.DefaultSettingValueAttribute("Events")] - public string CollectionMode { - get { - return ((string)(this["CollectionMode"])); - } - set { - this["CollectionMode"] = value; - } - } - } -} diff --git a/Properties/Settings.settings b/Properties/Settings.settings deleted file mode 100644 index 27069e6..0000000 --- a/Properties/Settings.settings +++ /dev/null @@ -1,39 +0,0 @@ - - - - - - LARMOR - - - 11 - - - 11 - - - Flat Plate - - - Alternate - TRANS first - - - 1 - - - 1 - - - False - - - Large = 25, 16 mm - - - Large = 25, 16 mm - - - Events - - - \ No newline at end of file diff --git a/SANS_Script_GUI_v4.csproj b/SANS_Script_GUI_v4.csproj deleted file mode 100644 index e8ed189..0000000 --- a/SANS_Script_GUI_v4.csproj +++ /dev/null @@ -1,185 +0,0 @@ - - - - - Debug - AnyCPU - {5DBFF2C3-05A0-4B4B-9080-8224D86A7B40} - WinExe - Properties - LOQ_Script_Gui - SANS_Script_Gui_v4 - v4.8 - 512 - {60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} - 4 - false - - publish\ - true - Disk - false - Foreground - 7 - Days - false - false - true - 0 - 1.0.0.%2a - false - true - - - x64 - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - false - - - x64 - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - - - SANS_Icon.ico - - - - True - - - - - - - - - - - 4.0 - - - - - - - - MSBuild:Compile - Designer - - - - - - PreviewDialog.xaml - - - - - - - - EstimationPanel.xaml - - - - - - - - - SettingsPanel.xaml - - - - - - - Designer - MSBuild:Compile - - - Designer - MSBuild:Compile - - - MSBuild:Compile - Designer - - - App.xaml - Code - - - MainWindow.xaml - Code - - - Designer - MSBuild:Compile - - - - - Code - - - True - True - Resources.resx - - - True - Settings.settings - True - - - ResXFileCodeGenerator - Resources.Designer.cs - - - SettingsSingleFileGenerator - Settings.Designer.cs - - - - - - - - - False - Microsoft .NET Framework 4.5 %28x86 and x64%29 - true - - - False - .NET Framework 3.5 SP1 - false - - - - - - - - - - - \ No newline at end of file diff --git a/SANS_Script_GUI_v4.csproj.py b/SANS_Script_GUI_v4.csproj.py deleted file mode 100644 index f312438..0000000 --- a/SANS_Script_GUI_v4.csproj.py +++ /dev/null @@ -1,174 +0,0 @@ -import xml.etree.ElementTree as ET - -root = ET.Element("Project", ToolsVersion="12.0", DefaultTargets="Build", xmlns="http://schemas.microsoft.com/developer/msbuild/2003") - -ET.SubElement(root, "Import", Project="$(MSBuildExtensionsPath)\\$(MSBuildToolsVersion)\\Microsoft.Common.props", Condition="Exists('$(MSBuildExtensionsPath)\\$(MSBuildToolsVersion)\\Microsoft.Common.props')") - -property_group = ET.SubElement(root, "PropertyGroup") -ET.SubElement(property_group, "Configuration", Condition=" '$(Configuration)' == '' ").text = "Debug" -ET.SubElement(property_group, "Platform", Condition=" '$(Platform)' == '' ").text = "AnyCPU" -ET.SubElement(property_group, "ProjectGuid").text = "{5DBFF2C3-05A0-4B4B-9080-8224D86A7B40}" -ET.SubElement(property_group, "OutputType").text = "WinExe" -ET.SubElement(property_group, "AppDesignerFolder").text = "Properties" -ET.SubElement(property_group, "RootNamespace").text = "LOQ_Script_Gui" -ET.SubElement(property_group, "AssemblyName").text = "SANS_Script_Gui_v4" -ET.SubElement(property_group, "TargetFrameworkVersion").text = "v4.8" -ET.SubElement(property_group, "FileAlignment").text = "512" -ET.SubElement(property_group, "ProjectTypeGuids").text = "{60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}" -ET.SubElement(property_group, "WarningLevel").text = "4" -ET.SubElement(property_group, "IsWebBootstrapper").text = "false" -ET.SubElement(property_group, "TargetFrameworkProfile") -ET.SubElement(property_group, "PublishUrl").text = "publish\\" -ET.SubElement(property_group, "Install").text = "true" -ET.SubElement(property_group, "InstallFrom").text = "Disk" -ET.SubElement(property_group, "UpdateEnabled").text = "false" -ET.SubElement(property_group, "UpdateMode").text = "Foreground" -ET.SubElement(property_group, "UpdateInterval").text = "7" -ET.SubElement(property_group, "UpdateIntervalUnits").text = "Days" -ET.SubElement(property_group, "UpdatePeriodically").text = "false" -ET.SubElement(property_group, "UpdateRequired").text = "false" -ET.SubElement(property_group, "MapFileExtensions").text = "true" -ET.SubElement(property_group, "ApplicationRevision").text = "0" -ET.SubElement(property_group, "ApplicationVersion").text = "1.0.0.%2a" -ET.SubElement(property_group, "UseApplicationTrust").text = "false" -ET.SubElement(property_group, "BootstrapperEnabled").text = "true" - -debug_property_group = ET.SubElement(root, "PropertyGroup", Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ") -ET.SubElement(debug_property_group, "PlatformTarget").text = "x64" -ET.SubElement(debug_property_group, "DebugSymbols").text = "true" -ET.SubElement(debug_property_group, "DebugType").text = "full" -ET.SubElement(debug_property_group, "Optimize").text = "false" -ET.SubElement(debug_property_group, "OutputPath").text = "bin\\Debug\\" -ET.SubElement(debug_property_group, "DefineConstants").text = "DEBUG;TRACE" -ET.SubElement(debug_property_group, "ErrorReport").text = "prompt" -ET.SubElement(debug_property_group, "WarningLevel").text = "4" -ET.SubElement(debug_property_group, "Prefer32Bit").text = "false" - -release_property_group = ET.SubElement(root, "PropertyGroup", Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ") -ET.SubElement(release_property_group, "PlatformTarget").text = "x64" -ET.SubElement(release_property_group, "DebugType").text = "pdbonly" -ET.SubElement(release_property_group, "Optimize").text = "true" -ET.SubElement(release_property_group, "OutputPath").text = "bin\\Release\\" -ET.SubElement(release_property_group, "DefineConstants").text = "TRACE" -ET.SubElement(release_property_group, "ErrorReport").text = "prompt" -ET.SubElement(release_property_group, "WarningLevel").text = "4" - -icon_property_group = ET.SubElement(root, "PropertyGroup") -ET.SubElement(icon_property_group, "ApplicationIcon").text = "SANS_Icon.ico" - -references = [ - ("Microsoft.Office.Interop.Excel, Version=15.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c, processorArchitecture=MSIL", "True"), - "ReachFramework", - "System", - "System.Data", - "System.Xml", - "Microsoft.CSharp", - "System.Core", - "System.Xml.Linq", - "System.Data.DataSetExtensions", - ("System.Xaml", "4.0"), - "WindowsBase", - "PresentationCore", - "PresentationFramework" -] - -item_group = ET.SubElement(root, "ItemGroup") -for ref in references: - if isinstance(ref, tuple): - reference = ET.SubElement(item_group, "Reference", Include=ref[0]) - ET.SubElement(reference, "EmbedInteropTypes").text = ref[1] - else: - ET.SubElement(item_group, "Reference", Include=ref) - -compile_files = [ - "Models\\DataCollection.py", - "Models\\InstrumentInfo.py", - "IO\\CsvExporter.py", - "Dialogs\\PreviewDialog.xaml.py", - "IO\\ExcelIO.py", - "Models\\A2Setting.py", - "Models\\Estimation.py", - "Models\\Geometry.py", - "EstimationPanel.xaml.py", - "Models\\Experiment.py", - "Models\\ExperimentSettings.py", - "Models\\WaitForUnits.py", - "Scripting\\ScriptGenerator.py", - "Scripting\\IScriptGenerator.py", - "Scripting\\OpenGenieScriptGenerator.py", - "SettingsPanel.xaml.py", - "ViewModels\\DataGridVM.py", - "ViewModels\\EstimationPanelVM.py", - "ViewModels\\InstrumentVM.py", - "ViewModels\\SettingsPanelVM.py", - "App.xaml.py", - "MainWindow.xaml.py", - "Properties\\AssemblyInfo.py", - "Properties\\Resources.Designer.py", - "Properties\\Settings.Designer.py" -] - -for file in compile_files: - compile = ET.SubElement(item_group, "Compile", Include=file) - if file.endswith(".xaml.py"): - ET.SubElement(compile, "DependentUpon").text = file.replace(".py", "") - elif file.endswith(".Designer.py"): - ET.SubElement(compile, "AutoGen").text = "True" - ET.SubElement(compile, "DesignTime").text = "True" - ET.SubElement(compile, "DependentUpon").text = file.replace(".Designer.py", ".resx") - if "Settings" in file: - ET.SubElement(compile, "DesignTimeSharedInput").text = "True" - -pages = [ - "Dialogs\\PreviewDialog.xaml", - "EstimationPanel.xaml", - "MainWindow.xaml", - "SettingsPanel.xaml" -] - -for page in pages: - page_element = ET.SubElement(item_group, "Page", Include=page) - ET.SubElement(page_element, "SubType").text = "Designer" - ET.SubElement(page_element, "Generator").text = "MSBuild:Compile" - -embedded_resources = [ - "Properties\\Resources.resx" -] - -for resource in embedded_resources: - embedded_resource = ET.SubElement(item_group, "EmbeddedResource", Include=resource) - ET.SubElement(embedded_resource, "Generator").text = "ResXFileCodeGenerator" - ET.SubElement(embedded_resource, "LastGenOutput").text = resource.replace(".resx", ".Designer.py") - -none_files = [ - "Properties\\Settings.settings", - "App.config" -] - -for none_file in none_files: - none = ET.SubElement(item_group, "None", Include=none_file) - if none_file.endswith(".settings"): - ET.SubElement(none, "Generator").text = "SettingsSingleFileGenerator" - ET.SubElement(none, "LastGenOutput").text = none_file.replace(".settings", ".Designer.py") - -ET.SubElement(item_group, "AppDesigner", Include="Properties\\") - -bootstrapper_packages = [ - (".NETFramework,Version=v4.5", "Microsoft .NET Framework 4.5 %28x86 and x64%29", "true"), - ("Microsoft.Net.Framework.3.5.SP1", ".NET Framework 3.5 SP1", "false") -] - -for package in bootstrapper_packages: - bootstrapper_package = ET.SubElement(item_group, "BootstrapperPackage", Include=package[0]) - ET.SubElement(bootstrapper_package, "Visible").text = "False" - ET.SubElement(bootstrapper_package, "ProductName").text = package[1] - ET.SubElement(bootstrapper_package, "Install").text = package[2] - -ET.SubElement(item_group, "Resource", Include="SANS_Icon.ico") - -ET.SubElement(item_group, "WCFMetadata", Include="Connected Services\\") - -ET.SubElement(root, "Import", Project="$(MSBuildToolsPath)\\Microsoft.CSharp.targets") - -tree = ET.ElementTree(root) -tree.write("SANS_Script_GUI_v4.xml", encoding="utf-8", xml_declaration=True) \ No newline at end of file diff --git a/SANS_Script_GUI_v4.csproj.user.py b/SANS_Script_GUI_v4.csproj.user.py deleted file mode 100644 index 13474b5..0000000 --- a/SANS_Script_GUI_v4.csproj.user.py +++ /dev/null @@ -1,16 +0,0 @@ -import xml.etree.ElementTree as ET - -root = ET.Element("Project", ToolsVersion="14.0", xmlns="http://schemas.microsoft.com/developer/msbuild/2003") - -property_group = ET.SubElement(root, "PropertyGroup") -ET.SubElement(property_group, "PublishUrlHistory").text = "publish\\" -ET.SubElement(property_group, "InstallUrlHistory") -ET.SubElement(property_group, "SupportUrlHistory") -ET.SubElement(property_group, "UpdateUrlHistory") -ET.SubElement(property_group, "BootstrapperUrlHistory") -ET.SubElement(property_group, "ErrorReportUrlHistory") -ET.SubElement(property_group, "FallbackCulture").text = "en-US" -ET.SubElement(property_group, "VerifyUploadedFiles").text = "false" - -tree = ET.ElementTree(root) -tree.write("SANS_Script_GUI_v4.xml", encoding="utf-8", xml_declaration=True) \ No newline at end of file diff --git a/SANS_Script_GUI_v4.xml b/SANS_Script_GUI_v4.xml deleted file mode 100644 index 5e50bb7..0000000 --- a/SANS_Script_GUI_v4.xml +++ /dev/null @@ -1,2 +0,0 @@ - -DebugAnyCPU{5DBFF2C3-05A0-4B4B-9080-8224D86A7B40}WinExePropertiesLOQ_Script_GuiSANS_Script_Gui_v4v4.8512{60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}4falsepublish\trueDiskfalseForeground7Daysfalsefalsetrue01.0.0.%2afalsetruex64truefullfalsebin\Debug\DEBUG;TRACEprompt4falsex64pdbonlytruebin\Release\TRACEprompt4SANS_Icon.icoTrue4.0Dialogs\PreviewDialog.xamlEstimationPanel.xamlSettingsPanel.xamlApp.xamlMainWindow.xamlTrueTrueProperties\Resources.resxTrueTrueProperties\Settings.resxTrueDesignerMSBuild:CompileDesignerMSBuild:CompileDesignerMSBuild:CompileDesignerMSBuild:CompileResXFileCodeGeneratorProperties\Resources.Designer.csSettingsSingleFileGeneratorProperties\Settings.Designer.csFalseMicrosoft .NET Framework 4.5 %28x86 and x64%29trueFalse.NET Framework 3.5 SP1false \ No newline at end of file diff --git a/SANS_Script_Writer/SANS_Script_Writer.sln b/SANS_Script_Writer/SANS_Script_Writer.sln deleted file mode 100644 index d5ff645..0000000 --- a/SANS_Script_Writer/SANS_Script_Writer.sln +++ /dev/null @@ -1,20 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 17 -VisualStudioVersion = 17.12.35527.113 d17.12 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{888888A0-9F3D-457C-B088-3A5042F75D52}") = "SANS_Script_Writer", "SANS_Script_Writer\SANS_Script_Writer.pyproj", "{83C517F7-94B7-490E-9812-1A281BC3AABB}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {83C517F7-94B7-490E-9812-1A281BC3AABB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {83C517F7-94B7-490E-9812-1A281BC3AABB}.Release|Any CPU.ActiveCfg = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection -EndGlobal diff --git a/SANS_Script_Writer/SANS_Script_Writer/SANS_Script_Writer.py b/SANS_Script_Writer/SANS_Script_Writer/SANS_Script_Writer.py deleted file mode 100644 index 8b13789..0000000 --- a/SANS_Script_Writer/SANS_Script_Writer/SANS_Script_Writer.py +++ /dev/null @@ -1 +0,0 @@ - diff --git a/SANS_Script_Writer/SANS_Script_Writer/SANS_Script_Writer.pyproj b/SANS_Script_Writer/SANS_Script_Writer/SANS_Script_Writer.pyproj deleted file mode 100644 index b77f107..0000000 --- a/SANS_Script_Writer/SANS_Script_Writer/SANS_Script_Writer.pyproj +++ /dev/null @@ -1,35 +0,0 @@ - - - Debug - 2.0 - 83c517f7-94b7-490e-9812-1a281bc3aabb - . - SANS_Script_Writer.py - - - . - . - SANS_Script_Writer - SANS_Script_Writer - - - true - false - - - true - false - - - - - - - - - - - - \ No newline at end of file diff --git a/Scripting/GeniePythonScriptGenerator.cs b/Scripting/GeniePythonScriptGenerator.cs deleted file mode 100644 index 3db9a71..0000000 --- a/Scripting/GeniePythonScriptGenerator.cs +++ /dev/null @@ -1,540 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Collections.ObjectModel; - -namespace LOQ_Script_Gui.Scripting -{ - class GeniePythonScriptGenerator : IScriptGenerator - { - string indent = " "; - - private bool isLarmor = false; - private bool isloq = false; - private bool issans2d = false; - private bool iszoom = false; - - public GeniePythonScriptGenerator(bool isLarmor, bool isloq, bool isZoom, bool isSans2d) - { - this.isLarmor = isLarmor; - this.isloq = isloq; - this.issans2d = isSans2d; - this.iszoom = isZoom; - } - - public string GenerateScript(ObservableCollection runs, ExperimentSettings settings, String scriptname) - { - string script = "# Script created by SANSScript at " + DateTime.Now + Environment.NewLine; - script += Environment.NewLine; - script += GenerateHeader(scriptname); - script += GenerateSetup(settings); - script += GenerateRuns(runs, settings); - script += Environment.NewLine; - - return script; - } - - private string GetIndentString(int n) - { - return new StringBuilder().Insert(0, indent, n).ToString(); - } - - private string GenerateHeader(String scriptname) - { - //string script = "import LSS.SANSroutines_v2 as lm" + Environment.NewLine ; - //string script = "from SansScripting import *" + Environment.NewLine; - //script += "@user_script" + Environment.NewLine + Environment.NewLine; - //script += Environment.NewLine; - string script = "import sys" + Environment.NewLine; - script += "from genie_python import genie as g, BLOCK_NAMES as b" + Environment.NewLine; - script += "sys.path.append(r\"c:\\instrument\\scripts\")" + Environment.NewLine; - - if (isLarmor) - { - script += "from instrument.larmor import * # pylint: disable=wildcard-import, unused-wildcard-import" + Environment.NewLine; - } - if (isloq) - { - script += "from instrument.loq import * # pylint: disable=wildcard-import, unused-wildcard-import" + Environment.NewLine; - } - if (iszoom) - { - script += "from instrument.zoom import * # pylint: disable=wildcard-import, unused-wildcard-import" + Environment.NewLine; - } - if (issans2d) - { - script += "from instrument.sans2d import * # pylint: disable=wildcard-import, unused-wildcard-import" + Environment.NewLine; - } - - script += Environment.NewLine; - script += "def "+scriptname+"():" + Environment.NewLine; - - return script; - } - - private string GenerateSetup(ExperimentSettings settings) - { - string script = ""; - - // TODO: Set number of available periods - - //script += GetIndentString(1) + "lm.setuplarmor_normal()" + Environment.NewLine; - script += GetIndentString(1) + string.Format("g.change_sample_par('Width', '{0}')", settings.SampleWidth) + Environment.NewLine; - script += GetIndentString(1) + string.Format("g.change_sample_par('height', '{0}')", settings.SampleHeight) + Environment.NewLine; - script += GetIndentString(1) + string.Format("g.change_sample_par('Geometry', '{0}')", settings.SampleGeometry) + Environment.NewLine + Environment.NewLine; - //script += GetIndentString(1) + string.Format("set_pv('IN:LARMOR:PARS:SAMPLE:WIDTH:SP', '{0}')", settings.SampleWidth) + Environment.NewLine; - //script += GetIndentString(1) + string.Format("set_pv('IN:LARMOR:PARS:SAMPLE:HEIGHT:SP', '{0}')", settings.SampleHeight) + Environment.NewLine; - //script += GetIndentString(1) + string.Format("set_pv('IN:LARMOR:PARS:SAMPLE:GEOMETRY:SP', '{0}')", settings.SampleGeometry) + Environment.NewLine + Environment.NewLine; - - return script; - } - - private string CheckPositions(ObservableCollection runs) - { - string script = ""; - - // Check that the positions are vailid - if (isLarmor) - { - string positionlist = "\""; - - foreach (Experiment exp in runs) - { - if (!string.IsNullOrWhiteSpace(exp.Position)) - { - positionlist += exp.Position + ","; - } - } - - if (positionlist.Length > 1) - { - if (positionlist.EndsWith(",")) - { - positionlist = positionlist.Substring(0, positionlist.Length - 1); - } - - positionlist += "\""; - - string tscript = string.Format("if check_move_pos([{0}]) != True", positionlist); - tscript = tscript.Replace(",", "\",\""); - script += GetIndentString(1) + tscript; - script += Environment.NewLine; - script += GetIndentString(2)+"print \"Invalid positions - script aborted\"" + Environment.NewLine; - script += GetIndentString(2)+"return" + Environment.NewLine + Environment.NewLine; - } - } - - //return script; - return ""; - } - private string GenerateRuns(ObservableCollection runs, ExperimentSettings settings) - { - string script = ""; - script = CheckPositions(runs); - - if (runs.Count == 0) - { - // Nothing to do - return script; - } - - if (settings.Order == RunOrder.AllTrans) - { - // All TRANS first - script += GetIndentString(1) + string.Format("for i in range({0}):", settings.NumTrans) + Environment.NewLine; - - foreach (Experiment exp in runs) - { - if (exp.Trans != 0) - { - int indent = 2; - script = DoTransRun(script, exp, indent, settings); - } - } - - // Then the SANS - script += GetIndentString(1) + string.Format("for i in range({0}):", settings.NumSans) + Environment.NewLine; - - foreach (Experiment exp in runs) - { - if (exp.Sans != 0) - { - int indent = 2; - script = DoSansRun(script, exp, indent, settings); - } - } - } - else if (settings.Order == RunOrder.AllSans) - { - // All SANS first - script += GetIndentString(1) + string.Format("for i in range({0}):", settings.NumSans) + Environment.NewLine; - - foreach (Experiment exp in runs) - { - if (exp.Sans != 0) - { - int indent = 2; - script = DoSansRun(script, exp, indent, settings); - } - } - - // Then the TRANS if we are running them - // check to see if there are actually any trans runs required by adding up all the trans count times - double sumTrans = 0.0; - foreach (Experiment exp in runs) - { - if (exp.Trans != 0) - { - sumTrans += exp.Trans; - } - } - - if (settings.NumTrans != 0 && sumTrans > 0.01) - { - script += GetIndentString(1) + string.Format("for i in range({0}):", settings.NumTrans) + Environment.NewLine; - } - foreach (Experiment exp in runs) - { - if (exp.Trans != 0) - { - int indent = 2; - script = DoTransRun(script, exp, indent, settings); - } - } - } - else if (settings.Order == RunOrder.TransFirst || settings.Order == RunOrder.SansFirst) - { - // TRANS - SANS - TRANS - SANS etc. or SANS - TRANS - SANS - TRANS etc - // Note: number of SANS and TRANS runs may be different - script += GetIndentString(1) + string.Format("num_sans = {0}", settings.NumSans) + Environment.NewLine; - script += GetIndentString(1) + string.Format("num_trans = {0}", settings.NumTrans) + Environment.NewLine; - script += GetIndentString(1) + "count = 0" + Environment.NewLine; - script += GetIndentString(1) + "while True:" + Environment.NewLine; - - int indent = 2; - - foreach (Experiment exp in runs) - { - if (settings.Order == RunOrder.TransFirst) - { - if (exp.Trans != 0) - { - script += GetIndentString(indent) + "if count < num_trans:" + Environment.NewLine; - script = DoTransRun(script, exp, indent + 1, settings); - } - if (exp.Sans != 0) - { - script += GetIndentString(indent) + "if count < num_sans:" + Environment.NewLine; - script = DoSansRun(script, exp, indent + 1, settings); - } - } - else - { - if (exp.Sans != 0) - { - script += GetIndentString(indent) + "if count < num_sans:" + Environment.NewLine; - script = DoSansRun(script, exp, indent + 1, settings); - } - if (exp.Trans != 0) - { - script += GetIndentString(indent) + "if count < num_trans:" + Environment.NewLine; - script = DoTransRun(script, exp, indent + 1, settings); - } - } - } - - script += GetIndentString(indent) + "count += 1" + Environment.NewLine; - script += GetIndentString(indent) + "if count >= num_trans and count >= num_sans: break" + Environment.NewLine; - } - - return script; - } - - private string DoTransRun(string script, Experiment exp, int indent, ExperimentSettings settings) - { - script += DoSampleEnvironment(exp, indent); - //script += SetAperture(settings.A2SettingTrans, indent); - - string waitfor = "uamps"; // Default to uamps - string rtype = "histogram"; // Default to event mode - string fulltitle = "{" + exp.Sample + "}"; - - if (settings.CollectionMode == DataCollection.Events) - { - rtype = "event"; - } - - if (exp.TransWait == WaitForUnits.Frames) - { - waitfor = "frames"; - } - else if (exp.TransWait == WaitForUnits.Seconds) - { - waitfor = "seconds"; - } - else if (exp.TransWait == WaitForUnits.Minutes) - { - waitfor = "minutes"; - } - - if (isLarmor == true || iszoom == true) - { - script += GetIndentString(indent) + string.Format("do_trans(title='{1}',pos='{0}', {2}={3}, thickness={4})", - exp.Position, fulltitle, waitfor, exp.Trans, exp.Thickness) + Environment.NewLine; - } - else if(isloq == true) - { - script += GetIndentString(indent) + string.Format("do_trans(title='{1}',pos='{0}', {2}={3}, thickness={4}, aperture='small')", - exp.Position, fulltitle, waitfor, exp.Trans, exp.Thickness) + Environment.NewLine; - } - else - { - script += GetIndentString(indent) + string.Format("do_trans(title='{1}',pos='{0}', {2}={3}, thickness={4}, dae='transmission', aperture='{5}')", - exp.Position, fulltitle, waitfor, exp.Trans, exp.Thickness,ApertureString(settings.A2SettingTrans)) + Environment.NewLine; - } - - script += GetIndentString(indent) + exp.PostCommand + Environment.NewLine; - return script; - } - - private string DoSansRun(string script, Experiment exp, int indent, ExperimentSettings settings) - { - script += DoSampleEnvironment(exp, indent); - //script += SetAperture(settings.A2SettingSans, indent); - - string waitfor = "uamps"; // Default to uamps - string rtype = "histogram"; // Default to histogram mode - string fulltitle = "{" + exp.Sample + "}"; - - if (!string.IsNullOrWhiteSpace(exp.Background)) - { - fulltitle += "_{" + exp.Background + "}"; - } - - if (settings.CollectionMode == DataCollection.Events || settings.CollectionMode == DataCollection.TRANSSANS_Events) - { - rtype = "event"; - } - else if (settings.CollectionMode == DataCollection.SESANS) - { - rtype = "sesans"; - } - if (settings.CollectionMode == DataCollection.TRANSSANS_Events || settings.CollectionMode == DataCollection.TRANSSANS_Histogram) - { - // add TRANS to the title so that when SANS is added by do_sans we get _TRANS_SANS - fulltitle += "_TRANS"; - } - - if (exp.SansWait == WaitForUnits.Frames) - { - waitfor = "frames"; - } - else if (exp.SansWait == WaitForUnits.Seconds) - { - waitfor = "seconds"; - } - else if (exp.SansWait == WaitForUnits.Minutes) - { - waitfor = "minutes"; - } - - if (isLarmor == true || iszoom == true) - { - script += GetIndentString(indent) + string.Format("do_sans(title='{1}',pos='{0}', {2}={3}, thickness={4}, dae='{5}')", - exp.Position, fulltitle, waitfor, exp.Sans, exp.Thickness, rtype) + Environment.NewLine; - } - else - { - script += GetIndentString(indent) + string.Format("do_sans(title='{1}',pos='{0}', {2}={3}, thickness={4}, dae='{5}',aperture='{6}')", - exp.Position, fulltitle, waitfor, exp.Sans, exp.Thickness, rtype,ApertureString(settings.A2SettingSans)) + Environment.NewLine; - } - - script += GetIndentString(indent) + exp.PostCommand + Environment.NewLine; - return script; - } - - private string SetAperture(string size, int indent) - { - string script = ""; - if (size == A2Setting.Large || size == A2Setting.Larmor) - { - script += GetIndentString(indent) + "set_aperture('large')" + Environment.NewLine; - } - else if (size == A2Setting.Medium || size == A2Setting.Larmor) - { - script += GetIndentString(indent) + "set_aperture('medium')" + Environment.NewLine; - } - else if (size == A2Setting.Small || size == A2Setting.Larmor) - { - script += GetIndentString(indent) + "set_aperture('small')" + Environment.NewLine; - } - else if (size == A2Setting.XLarge || size == A2Setting.Larmor) - { - script += GetIndentString(indent) + "set_aperture('xlarge')" + Environment.NewLine; - } - return script; - } - - private string ApertureString(string size) - { - string script = ""; - if (size == A2Setting.Large || size == A2Setting.Larmor) - { - script = "large"; - } - else if (size == A2Setting.Medium || size == A2Setting.Larmor) - { - script = "medium"; - } - else if (size == A2Setting.Small || size == A2Setting.Larmor) - { - script = "small"; - } - else if (size == A2Setting.XLarge || size == A2Setting.Larmor) - { - script = "xlarge"; - } - return script; - } - - private string DoSampleEnvironment(Experiment exp, int indent) - { - string script = ""; - - // Set Temps first - if (!string.IsNullOrWhiteSpace(exp.Temperature1)) - { - double temp1 = Convert.ToDouble(exp.Temperature1); - double temp1low = temp1-1.0; - double temp1high = temp1 + 1.0; - if (isloq == true) - { - script += GetIndentString(indent) + string.Format("g.cset(internal_setpoint_1={0})", exp.Temperature1) + Environment.NewLine; - script += GetIndentString(indent) + string.Format("g.cset(\"Julabo_1_ext\",lowlimit={0},highlimit={1},runcontrol=True)", Convert.ToString(temp1low), Convert.ToString(temp1high)) + Environment.NewLine; - }else - { - script += GetIndentString(indent) + string.Format("g.cset(JULABO1_SP={0})", exp.Temperature1) + Environment.NewLine; - script += GetIndentString(indent) + string.Format("g.cset(\"JULABO1_TEMP\",lowlimit={0},highlimit={1},runcontrol=True)", Convert.ToString(temp1low), Convert.ToString(temp1high)) + Environment.NewLine; - } - } - - if (!string.IsNullOrWhiteSpace(exp.Temperature2)) - { - double temp2 = Convert.ToDouble(exp.Temperature2); - double temp2low = temp2 - 1.0; - double temp2high = temp2 + 1.0; - if (isloq == true) - { - script += GetIndentString(indent) + string.Format("g.cset(internal_setpoint_2={0})", exp.Temperature2) + Environment.NewLine; - script += GetIndentString(indent) + string.Format("g.cset(\"Julabo_2_ext\",lowlimit={0},highlimit={1},runcontrol=True)", Convert.ToString(temp2low), Convert.ToString(temp2high)) + Environment.NewLine; - } - else - { - script += GetIndentString(indent) + string.Format("g.cset(JULABO2_SP={0})", exp.Temperature2) + Environment.NewLine; - script += GetIndentString(indent) + string.Format("g.cset(\"JULABO2_TEMP\",lowlimit={0},highlimit={1},runcontrol=True)", Convert.ToString(temp2low), Convert.ToString(temp2high)) + Environment.NewLine; - } - } - - // Check for pre-command - always run immediately after the temp is set - if (!string.IsNullOrWhiteSpace(exp.PreCommand)) - { - script += GetIndentString(indent) + exp.PreCommand + Environment.NewLine; - } - - if (!string.IsNullOrWhiteSpace(exp.Field)) - { - script += GetIndentString(indent) + string.Format("g.cset(FIELD={0})", exp.Field) + Environment.NewLine; - } - - if (!string.IsNullOrWhiteSpace(exp.ShearRate1)) - { - script += GetIndentString(indent) + string.Format("g.cset(SHEAR_RATE_1={0})", exp.ShearRate1) + Environment.NewLine; - } - - if (!string.IsNullOrWhiteSpace(exp.ShearRate2)) - { - script += GetIndentString(indent) + string.Format("g.cset(SHEAR_RATE_2={0})", exp.ShearRate2) + Environment.NewLine; - } - - if (!string.IsNullOrWhiteSpace(exp.ShearAngle1)) - { - script += GetIndentString(indent) + string.Format("g.cset(SHEAR_ANGLE_1={0})", exp.ShearAngle1) + Environment.NewLine; - } - - if (!string.IsNullOrWhiteSpace(exp.ShearAngle2)) - { - script += GetIndentString(indent) + string.Format("g.cset(SHEAR_ANGLE_2={0})", exp.ShearAngle1) + Environment.NewLine; - } - - return script; - } - - public string EstimateRunTime(ObservableCollection runs, ExperimentSettings settings, Estimation estimation) - { - double minutes = 0; - - if (settings.NumSans > 0) - { - minutes += CalculateTime(runs, true, estimation.CountRate, estimation.MoveTime) * settings.NumSans; - } - - if (settings.NumTrans > 0) - { - minutes += CalculateTime(runs, false, estimation.CountRate, estimation.MoveTime) * settings.NumTrans; - } - - DateTime time = new DateTime(1, 1, 1, 0, 0, 0); - - time = time.AddMinutes(minutes); - - return time.ToLongTimeString(); - } - - private static double CalculateTime(ObservableCollection runs, bool isSans, double countRate, double secondsBetween) - { - double minutes = 0; - - foreach (Experiment exp in runs) - { - if (!string.IsNullOrWhiteSpace(exp.Position)) - { - if (isSans) - { - minutes += GetMinutes(exp.SansWait, exp.Sans, countRate); - } - else - { - minutes += GetMinutes(exp.TransWait, exp.Trans, countRate); - } - - minutes += secondsBetween / 60; - } - } - - return minutes; - } - - private static double GetMinutes(string wait, double length, double countRate) - { - if (wait == WaitForUnits.Minutes) - { - return length; - } - else if (wait == WaitForUnits.Seconds) - { - return length / 60.0; - } - else if (wait == WaitForUnits.Frames) - { - return length / (25.0 * 60.0); - } - else - { - // If in doubt use micro-amps - return length / (countRate / 60.0); - } - } - } -} diff --git a/Scripting/IScriptGenerator.cs b/Scripting/IScriptGenerator.cs deleted file mode 100644 index fdddd91..0000000 --- a/Scripting/IScriptGenerator.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System; -using System.Collections.ObjectModel; - -namespace LOQ_Script_Gui.Scripting -{ - interface IScriptGenerator - { - string GenerateScript(ObservableCollection runs, ExperimentSettings settings, String scriptname); - string EstimateRunTime(ObservableCollection runs, ExperimentSettings settings, Estimation estimation); - } -} diff --git a/Scripting/OpenGenieScriptGenerator.cs b/Scripting/OpenGenieScriptGenerator.cs deleted file mode 100644 index 9c9f5b7..0000000 --- a/Scripting/OpenGenieScriptGenerator.cs +++ /dev/null @@ -1,627 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Collections.ObjectModel; - -namespace LOQ_Script_Gui.Scripting -{ - class OpenGenieScriptGenerator : IScriptGenerator - { - private bool isSans2d = false; - - public OpenGenieScriptGenerator(bool isSans2d) - { - this.isSans2d = isSans2d; - } - - public string GenerateScript(ObservableCollection runs, ExperimentSettings settings, String scriptname) - { - string script = "# Script created by LOQ Script at " + DateTime.Now + Environment.NewLine; - script += "# Script MUST be loaded in to Open GENIE using the loadscript command." + Environment.NewLine + Environment.NewLine; - - if (isSans2d) - { - script += "QUALIFIERS /test" + Environment.NewLine; - } - - script += string.Format("LOCAL transCount = 0 sansCount = 0 numTRANS={0} numSANS={1}", settings.NumTrans, settings.NumSans); - script += Environment.NewLine + Environment.NewLine; - - script += "SETSCRIPTNAME THIS_PROCEDURE()" + Environment.NewLine + Environment.NewLine; - - script += string.Format("Sample_par width={0} height={1} geometry=\"{2}\"", settings.SampleWidth, settings.SampleHeight, settings.SampleGeometry); - script += Environment.NewLine + Environment.NewLine; - - if (isSans2d) - { - string positionlist = "\""; - - foreach (Experiment exp in runs) - { - if (!string.IsNullOrWhiteSpace(exp.Position)) - { - positionlist += exp.Position + ","; - } - } - - if (positionlist.Length > 1) - { - if (positionlist.EndsWith(",")) - { - positionlist = positionlist.Substring(0, positionlist.Length - 1); - } - - positionlist += "\""; - - script += string.Format("IF NOT CHECK_MOVE_POS({0})", positionlist); - script += Environment.NewLine; - script += " PRINTEN \"Invalid positions - script aborted\"" + Environment.NewLine; - script += " RETURN" + Environment.NewLine; - script += "ENDIF" + Environment.NewLine + Environment.NewLine; - - } - - script += "IF test" + Environment.NewLine; - script += " RETURN" + Environment.NewLine; - script += "ENDIF" + Environment.NewLine + Environment.NewLine; - } - - if (settings.Order == RunOrder.AllTrans) - { - // Do all trans first - if (settings.LoopPerRun) - { - script += DoAllTransLoopEach(runs, settings, true, true); - script += DoAllSansLoopEach(runs, settings, true, true); - } - else - { - script += "LOOP" + Environment.NewLine + Environment.NewLine; - script += DoAllTrans(runs, settings, true, true); - script += DoAllSans(runs, settings, true, true); - script += "EXITIF ((transCount >= numTRANS) AND (sansCount >= numSANS))" + Environment.NewLine + Environment.NewLine; - script += "ENDLOOP" + Environment.NewLine + Environment.NewLine; - } - } - else if (settings.Order == RunOrder.TransFirst) - { - // Do Trans-Sans-Trans-Sans... - if (settings.LoopPerRun) - { - foreach (Experiment exp in runs) - { - script += DoSingleTransLoopEach(exp, settings, true, false); - script += DoSingleSansLoopEach(exp, settings, false, true); - } - } - else - { - script += "LOOP" + Environment.NewLine + Environment.NewLine; - - foreach (Experiment exp in runs) - { - script += DoSingleTrans(exp, settings, true, false); - script += DoSingleSans(exp, settings, false, true); - } - - script += "transCount = transCount + 1" + Environment.NewLine; - script += "sansCount = sansCount + 1" + Environment.NewLine + Environment.NewLine; - script += "EXITIF ((transCount >= numTRANS) AND (sansCount >= numSANS))" + Environment.NewLine + Environment.NewLine; - script += "ENDLOOP" + Environment.NewLine + Environment.NewLine; - } - } - else if (settings.Order == RunOrder.AllSans) - { - // Do All Sans first - if (settings.LoopPerRun) - { - script += DoAllSansLoopEach(runs, settings, true, true); - script += DoAllTransLoopEach(runs, settings, true, true); - } - else - { - script += "LOOP" + Environment.NewLine + Environment.NewLine; - script += DoAllSans(runs, settings, true, true); - script += DoAllTrans(runs, settings, true, true); - script += "EXITIF ((transCount >= numTRANS) AND (sansCount >= numSANS))" + Environment.NewLine + Environment.NewLine; - script += "ENDLOOP" + Environment.NewLine + Environment.NewLine; - } - } - else if (settings.Order == RunOrder.SansFirst) - { - // Do Sans-Trans-Sans-Trans... - if (settings.LoopPerRun) - { - foreach (Experiment exp in runs) - { - script += DoSingleSansLoopEach(exp, settings, true, false); - script += DoSingleTransLoopEach(exp, settings, false, true); - } - } - else - { - script += "LOOP" + Environment.NewLine + Environment.NewLine; - - foreach (Experiment exp in runs) - { - script += DoSingleSans(exp, settings, true, false); - script += DoSingleTrans(exp, settings, false, true); - } - - script += "transCount = transCount + 1" + Environment.NewLine; - script += "sansCount = sansCount + 1" + Environment.NewLine + Environment.NewLine; - script += "EXITIF ((transCount >= numTRANS) AND (sansCount >= numSANS))" + Environment.NewLine + Environment.NewLine; - script += "ENDLOOP" + Environment.NewLine + Environment.NewLine; - } - } - - //script += "ENDPROCEDURE" + Environment.NewLine; - script += "PRINTN \"SCRIPT COMPLETED \"" + Environment.NewLine + Environment.NewLine; - - return script; - } - - private static string DoSingleSans(Experiment exp, ExperimentSettings settings, bool doPreCom, bool doPostCom) - { - string script = ""; - - if (settings.NumSans > 0) - { - if (exp.Sans > 0) - { - script += "IF (sansCount < numSANS)" + Environment.NewLine; - script += setSansAperture(settings.A2SettingSans); - script += DoSingleRun(false, exp, false, doPreCom, doPostCom, settings); - script += Environment.NewLine + "ENDIF" + Environment.NewLine + Environment.NewLine; - } - } - return script; - } - - private static string DoSingleTrans(Experiment exp, ExperimentSettings settings, bool doPreCom, bool doPostCom) - { - string script = ""; - - if (settings.NumTrans > 0) - { - if (exp.Trans > 0) - { - script += "IF (transCount < numTRANS)" + Environment.NewLine; - script += setTransAperture(settings.A2SettingTrans); - script += DoSingleRun(true, exp, false, doPreCom, doPostCom, settings); - script += Environment.NewLine + "ENDIF" + Environment.NewLine + Environment.NewLine; - } - } - return script; - } - - private static string DoSingleSansLoopEach(Experiment exp, ExperimentSettings settings, bool doPreCom, bool doPostCom) - { - string script = ""; - - if (settings.NumSans > 0) - { - if (exp.Sans > 0) - { - script += setSansAperture(settings.A2SettingSans); - script += DoSingleRun(false, exp, true, doPreCom, doPostCom, settings) + Environment.NewLine; - } - } - return script; - } - - private static string DoSingleTransLoopEach(Experiment exp, ExperimentSettings settings, bool doPreCom, bool doPostCom) - { - string script = ""; - - if (settings.NumTrans > 0) - { - if (exp.Trans > 0) - { - script += setTransAperture(settings.A2SettingTrans); - script += DoSingleRun(true, exp, true, doPreCom, doPostCom,settings) + Environment.NewLine; - } - } - return script; - } - - private static string DoAllSans(ObservableCollection runs, ExperimentSettings settings, bool doPreCom, bool doPostCom) - { - string script = ""; - - if (settings.NumSans > 0) - { - script += setSansAperture(settings.A2SettingSans); - - script += "IF sansCount < numSANS" + Environment.NewLine + Environment.NewLine; - - // Loop through each experiment - foreach (Experiment exp in runs) - { - script += DoSingleRun(false, exp, false, doPreCom, doPostCom, settings) + Environment.NewLine; - } - - script += "sansCount = sansCount + 1" + Environment.NewLine + Environment.NewLine; - script += "ENDIF" + Environment.NewLine + Environment.NewLine; - } - - return script; - } - - private static string DoAllTrans(ObservableCollection runs, ExperimentSettings settings, bool doPreCom, bool doPostCom) - { - string script = ""; - - if (settings.NumTrans > 0) - { - script += setTransAperture(settings.A2SettingTrans); - - script += "IF transCount < numTRANS" + Environment.NewLine + Environment.NewLine; - - // Loop through each experiment - foreach (Experiment exp in runs) - { - script += DoSingleRun(true, exp, false, doPreCom, doPostCom, settings) + Environment.NewLine; - } - - script += "transCount = transCount + 1" + Environment.NewLine + Environment.NewLine; - script += "ENDIF" + Environment.NewLine + Environment.NewLine; - } - - return script; - } - - private static string DoAllSansLoopEach(ObservableCollection runs, ExperimentSettings settings, bool doPreCom, bool doPostCom) - { - string script = ""; - - if (settings.NumSans > 0) - { - script += setSansAperture(settings.A2SettingSans); - - // Loop through each experiment - foreach (Experiment exp in runs) - { - script += DoSingleRun(false, exp, true, doPreCom, doPostCom, settings) + Environment.NewLine; - } - } - - return script; - } - - private static string DoAllTransLoopEach(ObservableCollection runs, ExperimentSettings settings, bool doPreCom, bool doPostCom) - { - string script = ""; - - if (settings.NumTrans > 0) - { - script += setTransAperture(settings.A2SettingTrans); - - // Loop through each experiment - foreach (Experiment exp in runs) - { - script += DoSingleRun(true, exp, true, doPreCom, doPostCom, settings) + Environment.NewLine; - } - } - - return script; - } - - private static string setSansAperture(string A2SizeSans) - { - string script = ""; - - if (A2SizeSans == A2Setting.Large) - { - script += "DO_SANS /LARGE" + Environment.NewLine + Environment.NewLine; - } - else if (A2SizeSans == A2Setting.Medium) - { - script += "DO_SANS /MEDIUM" + Environment.NewLine + Environment.NewLine; - } - else if (A2SizeSans == A2Setting.Small) - { - script += "DO_SANS /SMALL" + Environment.NewLine + Environment.NewLine; - } - else if (A2SizeSans == A2Setting.Trans) - { - script += "DO_SANS /TRANS" + Environment.NewLine + Environment.NewLine; - } - - return script; - } - - private static string setTransAperture(string A2SizeTrans) - { - string script = ""; - - if (string.IsNullOrWhiteSpace(A2SizeTrans)) - { - script += "DO_TRANS" + Environment.NewLine + Environment.NewLine; - } - else - { - if (A2SizeTrans == A2Setting.Large) - { - script += "DO_TRANS /LARGE" + Environment.NewLine + Environment.NewLine; - } - else if (A2SizeTrans == A2Setting.Medium) - { - script += "DO_TRANS /MEDIUM" + Environment.NewLine + Environment.NewLine; - } - else if (A2SizeTrans == A2Setting.Small) - { - script += "DO_TRANS /SMALL" + Environment.NewLine + Environment.NewLine; - } - else if (A2SizeTrans == A2Setting.Trans) - { - script += "DO_TRANS /TRANS" + Environment.NewLine + Environment.NewLine; - } - } - - return script; - } - - private static string DoSingleRun(bool isTrans, Experiment exp, bool isLoop, bool doPreCom, bool doPostCom, ExperimentSettings settings) - { - string script = ""; - - if (exp.Trans > 0 || exp.Sans > 0) - { - if (!string.IsNullOrWhiteSpace(exp.RbNumber)) - { - script += "change rbno=" + exp.RbNumber + Environment.NewLine; - } - - if (!string.IsNullOrWhiteSpace(exp.Position)) - { - if (doPreCom) - { - script += DoSampleEnvironment(exp); - } - - if (isLoop) - { - if (isTrans) - { - script += "LOOP transCount FROM 1 TO numTRANS" + Environment.NewLine; - } - else - { - script += "LOOP sansCount FROM 1 TO numSANS" + Environment.NewLine; - } - - script += CreateMoveCommand(isTrans, exp, settings); - script += "ENDLOOP" + Environment.NewLine; - } - else - { - script += CreateMoveCommand(isTrans, exp, settings); - } - - // Check for post-command - always run last - if (doPostCom && !string.IsNullOrWhiteSpace(exp.PostCommand)) - { - script += exp.PostCommand + Environment.NewLine; - } - } - } - - return script; - } - - private static string DoSampleEnvironment(Experiment exp) - { - string script = ""; - - // Set Temps first - if (!string.IsNullOrWhiteSpace(exp.Temperature1)) - { - script += "CSET TEMP=" + exp.Temperature1 + Environment.NewLine; - } - - if (!string.IsNullOrWhiteSpace(exp.Temperature2)) - { - script += "CSET TEMP2=" + exp.Temperature2 + Environment.NewLine; - } - - // Check for pre-command - always run immediately after the temp is set - if (!string.IsNullOrWhiteSpace(exp.PreCommand)) - { - script += exp.PreCommand + Environment.NewLine; - } - - if (!string.IsNullOrWhiteSpace(exp.Field)) - { - script += "CSET FIELD=" + exp.Field + Environment.NewLine; - } - - if (!string.IsNullOrWhiteSpace(exp.ShearRate1)) - { - script += "CSET SHEAR_RATE_1=" + exp.ShearRate1 + Environment.NewLine; - } - - if (!string.IsNullOrWhiteSpace(exp.ShearRate2)) - { - script += "CSET SHEAR_RATE_2=" + exp.ShearRate2 + Environment.NewLine; - } - - if (!string.IsNullOrWhiteSpace(exp.ShearAngle1)) - { - script += "CSET SHEAR_ANGLE_1=" + exp.ShearAngle1 + Environment.NewLine; - } - - if (!string.IsNullOrWhiteSpace(exp.ShearAngle2)) - { - script += "CSET SHEAR_ANGLE_2=" + exp.ShearAngle2 + Environment.NewLine; - } - - return script; - } - - private static string CreateMoveCommand(bool isTrans, Experiment exp, ExperimentSettings settings) - { - string script = "MOVE pos=\""+exp.Position+"\""; - string fulltitle = ""; - - - if (!string.IsNullOrWhiteSpace(exp.Sample)) - { - fulltitle = "{" + exp.Sample + "}"; - } - - if (!string.IsNullOrWhiteSpace(exp.Background)) - { - fulltitle += "_{"+exp.Background+"}"; - } - - if (!string.IsNullOrWhiteSpace(exp.Thickness)) - { - script += " thick=" + exp.Thickness; - } - else - { - script += " thick=1"; - } - - if (!string.IsNullOrWhiteSpace(exp.Period)) - { - script += " period=" + exp.Period; - } - - if (isTrans) - { - script += WriteWaitFor(exp.TransWait, exp.Trans); - - if (!string.IsNullOrWhiteSpace(fulltitle)) - { - script += string.Format(" title=\"{0}_TRANS\"", "{"+exp.Sample+"}"); - } - } - else - { - script += WriteWaitFor(exp.SansWait, exp.Sans); - - if (!string.IsNullOrWhiteSpace(fulltitle)) - { - if (settings.CollectionMode == DataCollection.TRANSSANS_Events || settings.CollectionMode == DataCollection.TRANSSANS_Histogram) - { - script += string.Format(" title=\"{0}_TRANS_SANS\"", fulltitle); - } - else - { - script += string.Format(" title=\"{0}_SANS\"", fulltitle); - } - } - } - - return script + Environment.NewLine; - - } - - private static string WriteWaitFor(string waitfor, double length) - { - string script = ""; - - if (!string.IsNullOrWhiteSpace(waitfor)) - { - if (waitfor.ToLower() == WaitForUnits.MicroAmps.ToLower()) - { - script += " uAhr=" + length; - } - else if (waitfor.ToLower() == WaitForUnits.Minutes.ToLower()) - { - script += " min=" + length; - } - else if (waitfor.ToLower() == WaitForUnits.Seconds.ToLower()) - { - script += " seconds=" + length; - } - else if (waitfor.ToLower() == WaitForUnits.Frames.ToLower()) - { - script += " frame=" + length; - } - else - { - // If in doubt use micro-amps - script += " uAhr=" + length; - } - } - else - { - // If in doubt use micro-amps - script += " uAhr=" + length; - } - return script; - } - - public string EstimateRunTime(ObservableCollection runs, ExperimentSettings settings, Estimation estimation) - { - double minutes = 0; - - if (settings.NumSans > 0) - { - minutes += CalculateTime(runs, true, estimation.CountRate, estimation.MoveTime) * settings.NumSans; - } - - if (settings.NumTrans > 0) - { - minutes += CalculateTime(runs, false, estimation.CountRate, estimation.MoveTime) * settings.NumTrans; - } - - DateTime time = new DateTime(1, 1, 1, 0, 0, 0); - - time = time.AddMinutes(minutes); - - return time.ToLongTimeString(); - } - - private static double CalculateTime(ObservableCollection runs, bool isSans, double countRate, double secondsBetween) - { - double minutes = 0; - - foreach (Experiment exp in runs) - { - if (!string.IsNullOrWhiteSpace(exp.Position)) - { - if (isSans) - { - minutes += GetMinutes(exp.SansWait, exp.Sans, countRate); - } - else - { - minutes += GetMinutes(exp.TransWait, exp.Trans, countRate); - } - - minutes += secondsBetween / 60; - } - } - - return minutes; - } - - private static double GetMinutes(string wait, double length, double countRate) - { - if (wait == WaitForUnits.Minutes) - { - return length; - } - else if (wait == WaitForUnits.Seconds) - { - return length / 60.0; - } - else if (wait == WaitForUnits.Frames) - { - return length / (25.0 * 60.0); - } - else - { - // If in doubt use micro-amps - return length / (countRate / 60.0); - } - } - - } -} diff --git a/SettingsPanel.xaml b/SettingsPanel.xaml deleted file mode 100644 index 8c63f8f..0000000 --- a/SettingsPanel.xaml +++ /dev/null @@ -1,72 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Loop over each run? - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/SettingsPanel.xaml.cs b/SettingsPanel.xaml.cs deleted file mode 100644 index 3e768e6..0000000 --- a/SettingsPanel.xaml.cs +++ /dev/null @@ -1,47 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Windows; -using System.Windows.Controls; -using System.Windows.Data; -using System.Windows.Documents; -using System.Windows.Input; -using System.Windows.Media; -using System.Windows.Media.Imaging; -using System.Windows.Navigation; -using System.Windows.Shapes; - -namespace LOQ_Script_Gui -{ - /// - /// Interaction logic for SettingsPanel.xaml - /// - public partial class SettingsPanel : UserControl - { - public event EventHandler OnPreviewClicked; - public event EventHandler OnWriteClicked; - - public SettingsPanel() - { - InitializeComponent(); - } - - private void btnPreviewScript_Click(object sender, RoutedEventArgs e) - { - if (OnPreviewClicked != null) - { - OnPreviewClicked(sender, e); - } - } - - private void btnWriteScript_Click(object sender, RoutedEventArgs e) - { - if (OnWriteClicked != null) - { - OnWriteClicked(sender, e); - } - } - } -} diff --git a/ViewModels/DataGridVM.cs b/ViewModels/DataGridVM.cs deleted file mode 100644 index 7dd4481..0000000 --- a/ViewModels/DataGridVM.cs +++ /dev/null @@ -1,41 +0,0 @@ -using System.Collections.ObjectModel; -using System.ComponentModel; - -namespace LOQ_Script_Gui -{ - class DataGridVM : INotifyPropertyChanged - { - public event PropertyChangedEventHandler PropertyChanged; - - protected void OnPropertyChanged(string name) - { - PropertyChangedEventHandler handler = PropertyChanged; - if (handler != null) - { - handler(this, new PropertyChangedEventArgs(name)); - } - } - - ObservableCollection runs = new ObservableCollection(); - public ObservableCollection Runs - { - get - { - return runs; - } - set - { - runs = value; - OnPropertyChanged("Runs"); - } - } - - public static ObservableCollection WaitForChoices - { - get - { - return WaitForUnits.Choices; - } - } - } -} diff --git a/ViewModels/EstimationPanelVM.cs b/ViewModels/EstimationPanelVM.cs deleted file mode 100644 index 441ae08..0000000 --- a/ViewModels/EstimationPanelVM.cs +++ /dev/null @@ -1,24 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Collections.ObjectModel; -using System.ComponentModel; - -namespace LOQ_Script_Gui -{ - class EstimationPanelVM : INotifyPropertyChanged - { - public event PropertyChangedEventHandler PropertyChanged; - - protected void OnPropertyChanged(string name) - { - PropertyChangedEventHandler handler = PropertyChanged; - if (handler != null) - { - handler(this, new PropertyChangedEventArgs(name)); - } - } - } -} diff --git a/ViewModels/InstrumentVM.cs b/ViewModels/InstrumentVM.cs deleted file mode 100644 index 84daf86..0000000 --- a/ViewModels/InstrumentVM.cs +++ /dev/null @@ -1,179 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Collections.ObjectModel; -using System.ComponentModel; - -namespace LOQ_Script_Gui -{ - class InstrumentVM : INotifyPropertyChanged - { - List loqApertureSizes = new List() - { - A2Setting.Large, A2Setting.Medium, A2Setting.Small - }; - - List sans2dApertureSizes = new List() - { - A2Setting.XLarge, A2Setting.Large, A2Setting.Medium, A2Setting.Small - }; - - List larmorApertureSizes = new List() - { - A2Setting.Larmor - //A2Setting.LarmorLarge, A2Setting.LarmorMedium, A2Setting.LarmorSmall - }; - - List zoomApertureSizes = new List() - { - A2Setting.Large, A2Setting.Medium, A2Setting.Small - }; - - public event PropertyChangedEventHandler PropertyChanged; - - protected void OnPropertyChanged(string name) - { - PropertyChangedEventHandler handler = PropertyChanged; - if (handler != null) - { - handler(this, new PropertyChangedEventArgs(name)); - } - } - - private bool isLarmor = true; - public bool IsLarmor - { - get - { - return isLarmor; - } - set - { - isLarmor = value; - if (value) - { - IsLoq = false; - IsSans2d = false; - IsZoom = false; - InstrumentInfo.ScriptGenerator = new Scripting.GeniePythonScriptGenerator(true,false,false,false); - InstrumentInfo.A2Label = "A1, S1 Setting:"; - InstrumentInfo.A2Choices = new ObservableCollection(larmorApertureSizes); - InstrumentInfo.A2TransEnabled = true; - InstrumentInfo.CollectionModeEnabled = true; - estimation.CountRate = 40; - //settings.A2SettingSans = A2Setting.LarmorMedium; - settings.A2SettingSans = Properties.Settings.Default.A2SettingSans; - //settings.A2SettingTrans = A2Setting.LarmorMedium; - settings.A2SettingTrans = Properties.Settings.Default.A2SettingTrans; - } - - OnPropertyChanged("IsLarmor"); - } - } - - private bool isLoq = false; - public bool IsLoq - { - get - { - return isLoq; - } - set - { - isLoq = value; - if (value) - { - IsLarmor = false; - IsSans2d = false; - IsZoom = false; - InstrumentInfo.ScriptGenerator = new Scripting.GeniePythonScriptGenerator(false,true,false,false); - InstrumentInfo.A2Label = "A2 Setting:"; - InstrumentInfo.A2Choices = new ObservableCollection(loqApertureSizes); - InstrumentInfo.A2TransEnabled = true; - InstrumentInfo.CollectionModeEnabled = true; - estimation.CountRate = 170; - //settings.A2SettingSans = A2Setting.Large; - settings.A2SettingSans = Properties.Settings.Default.A2SettingSans; - //settings.A2SettingTrans = A2Setting.Large; - settings.A2SettingTrans = Properties.Settings.Default.A2SettingTrans; - } - - OnPropertyChanged("IsLoq"); - } - } - - private bool isSans2d = false; - public bool IsSans2d - { - get - { - return isSans2d; - } - set - { - isSans2d = value; - if (value) - { - IsLarmor = false; - IsLoq = false; - IsZoom = false; - //InstrumentInfo.ScriptGenerator = new Scripting.OpenGenieScriptGenerator(true); - InstrumentInfo.ScriptGenerator = new Scripting.GeniePythonScriptGenerator(false, false, false, true); - InstrumentInfo.A2Label = "A6 Setting:"; - InstrumentInfo.A2Choices = new ObservableCollection(sans2dApertureSizes); - InstrumentInfo.A2TransEnabled = true; - InstrumentInfo.CollectionModeEnabled = true; - estimation.CountRate = 40; - //settings.A2SettingSans = A2Setting.Large; - settings.A2SettingSans = Properties.Settings.Default.A2SettingSans; - //settings.A2SettingTrans = A2Setting.Large; - settings.A2SettingTrans = Properties.Settings.Default.A2SettingTrans; - } - - OnPropertyChanged("IsSans2d"); - } - } - - private bool isZoom = false; - public bool IsZoom - { - get - { - return isZoom; - } - set - { - isZoom = value; - if (value) - { - IsLarmor = false; - IsLoq = false; - IsSans2d = false; - InstrumentInfo.ScriptGenerator = new Scripting.GeniePythonScriptGenerator(false,false,true,false); - InstrumentInfo.A2Label = "A6 Setting:"; - InstrumentInfo.A2Choices = new ObservableCollection(zoomApertureSizes); - InstrumentInfo.A2TransEnabled = true; - InstrumentInfo.CollectionModeEnabled = true; - estimation.CountRate = 40; - //settings.A2SettingSans = A2Setting.Large; - settings.A2SettingSans = Properties.Settings.Default.A2SettingSans; - //settings.A2SettingTrans = A2Setting.Large; - settings.A2SettingTrans = Properties.Settings.Default.A2SettingTrans; - } - - OnPropertyChanged("IsZoom"); - } - } - - ExperimentSettings settings; - Estimation estimation; - - public InstrumentVM(ExperimentSettings settings, Estimation estimation) - { - this.settings = settings; - this.estimation = estimation; - } - } -} diff --git a/ViewModels/InstrumentVM.py b/ViewModels/InstrumentVM.py deleted file mode 100644 index 5231a96..0000000 --- a/ViewModels/InstrumentVM.py +++ /dev/null @@ -1,36 +0,0 @@ -import tkinter as tk -from tkinter import StringVar, BooleanVar -from Models.A2Setting import A2Setting -from Models.DataCollection import DataCollection -from Models.InstrumentInfo import InstrumentInfo -from Models.Estimation import Estimation -from Models.ExperimentSettings import ExperimentSettings -from Scripting.ScriptGenerator import ScriptGenerator -from Properties.Settings import Settings - - -class InstrumentVM: - def __init__(self, experimentsettings=None, estimation=None): - #def __init__(self, experiment, estimate): - self.settings = experimentsettings if experimentsettings is not None else ExperimentSettings() - self.estimation = estimation if estimation is not None else Estimation() - - - - """ - self.is_larmor = False - self.is_loq = False - self.is_sans2d = False - self.is_zoom = False - """ - self.settings.update_instrument() - - -if __name__ == "__main__": - root = tk.Tk() - experimentsettings = ExperimentSettings() - estimation = Estimation() - vm = InstrumentVM(experimentsettings, estimation) - - vm.is_instrument.set("loq") - root.mainloop() \ No newline at end of file diff --git a/ViewModels/SettingsPanelVM.cs b/ViewModels/SettingsPanelVM.cs deleted file mode 100644 index d3b6b2d..0000000 --- a/ViewModels/SettingsPanelVM.cs +++ /dev/null @@ -1,87 +0,0 @@ -using System.Collections.ObjectModel; -using System.ComponentModel; -using System; - -namespace LOQ_Script_Gui -{ - class SettingsPanelVM : INotifyPropertyChanged - { - public event PropertyChangedEventHandler PropertyChanged; - - protected void OnPropertyChanged(string name) - { - PropertyChangedEventHandler handler = PropertyChanged; - if (handler != null) - { - handler(this, new PropertyChangedEventArgs(name)); - } - } - - // Provides the combo box options - public static ObservableCollection OrderChoices - { - get - { - return RunOrder.Choices; - } - } - - // Provides the combo box options - public static ObservableCollection GeometryChoices - { - get - { - return Geometry.Choices; - } - } - - // Provides the combo box options - public static ObservableCollection CollectionModeChoices - { - get - { - return DataCollection.Choices; - } - } - - ExperimentSettings experiment = new ExperimentSettings(); - public ExperimentSettings Experiment - { - get - { - return experiment; - } - set - { - experiment = value; - OnPropertyChanged("Experiment"); - } - } - - InstrumentInfo instInfo; - public InstrumentInfo InstInfo - { - get - { - return instInfo; - } - set - { - instInfo = value; - OnPropertyChanged("InstInfo"); - } - } - - public SettingsPanelVM() - { - // Need a default constructor to keep the databinding happy! - // The other constructor should be called ASAP - instInfo = new InstrumentInfo(); - } - - public SettingsPanelVM(InstrumentInfo instInfo) - { - this.instInfo = instInfo; - } - } -} From 51fbf61ac7090e58e35ad719340523e0c4990e66 Mon Sep 17 00:00:00 2001 From: Dirk Honecker Date: Tue, 31 Dec 2024 15:12:35 +0000 Subject: [PATCH 17/88] Connect and read settings --- EstimationPanel.py | 26 ++++++++++++----------- MainWindow.py | 40 +++++++++++++++++++----------------- Models/ExperimentSettings.py | 21 ++++++++++--------- Models/InstrumentInfo.py | 12 +++++------ Properties/AssemblyInfo.py | 2 +- Properties/Settings.ini | 6 +++--- Scripting/ScriptGenerator.py | 17 +++++++-------- SettingsPanel.py | 11 +++++----- 8 files changed, 70 insertions(+), 65 deletions(-) diff --git a/EstimationPanel.py b/EstimationPanel.py index e74f8e8..545a462 100644 --- a/EstimationPanel.py +++ b/EstimationPanel.py @@ -5,14 +5,16 @@ from Properties.Settings import Settings class EstimationPanel(ttk.Frame): - def __init__(self, parent, estimate = None): + def __init__(self, parent, estimation = None, settings = None): super().__init__(parent) self.on_calculate_clicked = None - self.estimate = estimate if estimate is not None else Estimation() - self.create_widgets() + self.estimation = estimation if estimation is not None else Estimation() + self.settings = settings if settings is not None else Settings() + + self.create_widgets() def create_widgets(self): - settings = Settings() + #self.pack(fill=tk.BOTH, expand=True) self.grid(sticky="nsew") @@ -27,8 +29,8 @@ def create_widgets(self): ttk.Label(grid_frame, text="Est. count rate:").grid(row=0, column=0, sticky=tk.E) count_rate_frame = ttk.Frame(grid_frame) count_rate_frame.grid(row=0, column=1, columnspan=2) - self.txt_count_rate = ttk.Entry(count_rate_frame, width=10, textvariable=self.estimate.count_rate) - self.txt_count_rate.insert(0, settings.count_rate) # Default value + self.txt_count_rate = ttk.Entry(count_rate_frame, width=10, textvariable=self.estimation.count_rate) + self.txt_count_rate.insert(0, self.settings.count_rate) # Default value self.txt_count_rate.grid(row=0, column=0, padx=5, pady=5) ttk.Label(count_rate_frame, text="µA/hr").grid(row=0, column=1) @@ -36,8 +38,8 @@ def create_widgets(self): ttk.Label(grid_frame, text="Est. time between moves:").grid(row=1, column=0, sticky=tk.E) move_time_frame = ttk.Frame(grid_frame) move_time_frame.grid(row=1, column=1, columnspan=2) - self.txt_move_time = ttk.Entry(move_time_frame, width=10, textvariable=self.estimate.move_time) - self.txt_move_time.insert(0, settings.move_time) # Default value + self.txt_move_time = ttk.Entry(move_time_frame, width=10, textvariable=self.estimation.move_time) + self.txt_move_time.insert(0, self.settings.move_time) # Default value self.txt_move_time.grid(row=0, column=0, padx=5, pady=5) ttk.Label(move_time_frame, text="seconds").grid(row=0, column=1) @@ -45,8 +47,8 @@ def create_widgets(self): ttk.Label(grid_frame, text="Est. script time:").grid(row=2, column=0, sticky=tk.E) script_time_frame = ttk.Frame(grid_frame) script_time_frame.grid(row=2, column=1, columnspan=2) - self.txt_estimate = ttk.Entry(script_time_frame, width=15, textvariable=self.estimate.script_time, state="readonly") - self.txt_estimate.grid(row=0, column=0, padx=5, pady=5) + self.txt_estimation = ttk.Entry(script_time_frame, width=15, textvariable=self.estimation.script_time, state="readonly") + self.txt_estimation.grid(row=0, column=0, padx=5, pady=5) # Calculate button self.btn_calculate = ttk.Button(grid_frame, text="Calculate", command=self.btn_calculate_click) @@ -60,8 +62,8 @@ def btn_calculate_click(self): if __name__ == "__main__": root = tk.Tk() - estimate = EstimationPanelVM() - panel = EstimationPanel(root, estimate) + estimation = EstimationPanelVM() + panel = EstimationPanel(root, estimation) panel.grid(sticky="nsew") root.grid_rowconfigure(0, weight=1) root.grid_columnconfigure(0, weight=1) diff --git a/MainWindow.py b/MainWindow.py index 25d20ae..dbcf52b 100644 --- a/MainWindow.py +++ b/MainWindow.py @@ -28,11 +28,15 @@ def __init__(self,parent): self.parent.geometry("800x600") self.data = DataGridVM() - self.inst_info = InstrumentInfo() - self.settings = SettingsPanel(self, inst_info=self.inst_info) - self.settings.experiment = ExperimentSettings() - self.estimate = Estimation() + self.settings = Settings() + self.estimation = Estimation() + self.settings.experiment = ExperimentSettings(settings=self.settings, estimation=self.estimation) + self.inst_info = InstrumentInfo(settings=self.settings) + + #self.settings.panel = SettingsPanel(self, inst_info=self.inst_info,settings=self.settings) + + #self.instrument = InstrumentVM(self.settings.experiment, self.estimate) self.create_widgets() @@ -58,14 +62,14 @@ def create_widgets(self): """ # Settings Panel - self.pnl_settings = SettingsPanel(self, self.settings) + self.pnl_settings = SettingsPanel(self, inst_info=self.inst_info,settings=self.settings) self.pnl_settings.grid(row=0, column=0, sticky="ew") self.pnl_settings.on_preview_clicked = self.pnl_settings_on_preview_clicked self.pnl_settings.on_write_clicked = self.pnl_settings_on_write_clicked # Estimation Panel - self.pnl_estimate = EstimationPanel(self, self.estimate) - self.pnl_estimate.grid(row=1, column=0, sticky="ew") - self.pnl_estimate.on_calculate_clicked = self.pnl_estimate_on_calculate_clicked + self.pnl_estimation = EstimationPanel(self, estimation=self.estimation,settings=self.settings) + self.pnl_estimation.grid(row=1, column=0, sticky="ew") + self.pnl_estimation.on_calculate_clicked = self.pnl_estimate_on_calculate_clicked # Buttons button_frame = ttk.Frame(self) button_frame.grid(row=2, column=0, padx=5, pady=5, sticky="w") @@ -97,9 +101,8 @@ def create_widgets(self): def onFrameConfigure(self, event): self.canvas.configure(scrollregion=self.canvas.bbox("all")) """ - def load_last_instrument(self): - settings = Settings() - inst = settings.last_instrument # Replace with actual settings retrieval + def load_last_instrument(self): + inst = self.settings.last_instrument # Replace with actual settings retrieval self.instrument_var.set(inst) @@ -115,14 +118,14 @@ def pnl_settings_on_write_clicked(self): file.write(script) def pnl_settings_on_preview_clicked(self): - script_generator= ScriptGenerator(self.instrument_var.get()) - script = script_generator.generate_script(self.data.runs, self.settings.experiment, "my_script") - print(script) - preview_dialog = PreviewDialog(self, script) + self.script_generator= ScriptGenerator(self.instrument_var.get()) + self.script = self.script_generator.generate_script(self.data.runs, self.settings.experiment, "my_script") + print(self.script) + preview_dialog = PreviewDialog(self, self.script) preview_dialog.show() def pnl_estimate_on_calculate_clicked(self): - self.estimate.script_time = self.inst_info.script_generator.estimate_run_time(self.data.runs, self.settings.experiment, self.estimate) + self.estimation.script_time = self.script_generator.estimate_run_time(self.data.runs, self.settings.experiment, self.estimation) def btn_create_new_table_click(self): self.data.runs.clear() @@ -155,9 +158,8 @@ def mnu_save_csv_click(self): messagebox.showerror("SANS Script - Error", "Could not save as CSV") def on_close(self): - # Save last_instrument to settings - settings = Settings() - settings.save() + # Save last_instrument to settings + self.settings.save() self.parent.destroy() diff --git a/Models/ExperimentSettings.py b/Models/ExperimentSettings.py index 4c65eeb..6aa4e8f 100644 --- a/Models/ExperimentSettings.py +++ b/Models/ExperimentSettings.py @@ -28,7 +28,7 @@ def __init__(self, settings=None, estimation=None): self.property_changed = [] self.is_instrument = tk.StringVar(value=self.settings.last_instrument) - + self.is_instrument.trace_add("write", self.update_instrument) self.is_instrument.trace_add("write", self.on_property_changed) self._order.trace_add("write", self.on_property_changed) @@ -52,6 +52,8 @@ def __init__(self, settings=None, estimation=None): self.sans2d_data_collection=DataCollection.get_SANS2D_choices() self.zoom_data_collection=DataCollection.get_ZOOM_choices() + self.update_instrument() + def __call__(self, order=None, num_sans=None, num_trans=None, loop_per_run=None, a2_setting_sans=None, a2_setting_trans=None, sample_geometry=None, sample_height=None, sample_width=None, collection_mode=None, is_valid=None): if order is not None: self.order = order @@ -92,17 +94,16 @@ def update_instrument(self, *args): self.set_instrument(self.is_instrument.get(), "A6 Setting:", self.zoom_aperture_sizes,"Data Acquisition:",self.zoom_data_collection, 40) def set_instrument(self, instrument, a2_label, a2_choices, data_collection_label, data_collection_choices, count_rate): - if not hasattr(self, 'settings') or self.settings is None: - self.settings = Settings() + - self.settings.is_instrument.set(instrument) + self.is_instrument.set(instrument) - self.settings.a2_label = a2_label - self.settings.a2_choices = a2_choices - self.settings.data_collection_label = data_collection_label - self.settings.data_collection_choices = data_collection_choices - self.settings.a2_trans_enabled = True - self.settings.collection_mode_enabled = True + self.a2_label = a2_label + self.a2_choices = a2_choices + self.data_collection_label = data_collection_label + self.data_collection_choices = data_collection_choices + self.a2_trans_enabled = True + self.collection_mode_enabled = True self.estimation.count_rate = count_rate diff --git a/Models/InstrumentInfo.py b/Models/InstrumentInfo.py index 1521f2a..896ba05 100644 --- a/Models/InstrumentInfo.py +++ b/Models/InstrumentInfo.py @@ -16,12 +16,12 @@ class InstrumentInfo: def __init__(self,settings=None): self.settings = settings if settings is not None else Settings() - self._a2_label = StringVar(value="A2 Setting:") - self._a2_choices = tk.Variable(value=["Large", "Medium", "Small"]) - self._data_collection_label = StringVar(value="Data acquisition:") - self._data_collection_choices = tk.Variable(value=["Event", "SESANS"]) - self._a2_trans_enabled = BooleanVar(value=True) - self._collection_mode_enabled = BooleanVar(value=False) + self._a2_label = StringVar(value=self.settings.experiment.a2_label) + self._a2_choices = tk.Variable(value=self.settings.experiment.a2_choices) + self._data_collection_label = StringVar(value=self.settings.experiment.data_collection_label) + self._data_collection_choices = tk.Variable(value=self.settings.experiment.data_collection_choices) + self._a2_trans_enabled = BooleanVar(value=self.settings.experiment.a2_trans_enabled) + self._collection_mode_enabled = BooleanVar(value=self.settings.experiment.collection_mode_enabled) self._script_generator = None diff --git a/Properties/AssemblyInfo.py b/Properties/AssemblyInfo.py index 223c844..e4c5d67 100644 --- a/Properties/AssemblyInfo.py +++ b/Properties/AssemblyInfo.py @@ -17,7 +17,7 @@ "License :: OSI Approved :: MIT License", "Operating System :: OS Independent", ], - python_requires='>=3.6', + python_requires=">=3.6", ) # Additional metadata for localization and resource management can be added as needed. \ No newline at end of file diff --git a/Properties/Settings.ini b/Properties/Settings.ini index b5b631d..51421f9 100644 --- a/Properties/Settings.ini +++ b/Properties/Settings.ini @@ -7,9 +7,9 @@ runorder = All Trans First numtrans = 1 numsans = 1 loopperrun = False -a2settingsans = -a2settingtrans = -collectionmode = +a2settingsans = Large +a2settingtrans = Large +collectionmode = Events tableentries = "POSITION, TRANS, TRANS_WAIT, SANS, SANS_WAIT, PERIOD, SAMPLE_IDENTIFICATION_STRING, BACKGROUND, THICKNESS, TEMPERATURE, TEMPERATURE2, FIELD, SHEAR_RATE_1, SHEAR_RATE_2, SHEAR_ANGLE_1, SHEAR_ANGLE_2, PRE_COMMAND, POST_COMMAND, RB_NUMBER" countrate = 38 movetime = 120 diff --git a/Scripting/ScriptGenerator.py b/Scripting/ScriptGenerator.py index 16e05fc..963df75 100644 --- a/Scripting/ScriptGenerator.py +++ b/Scripting/ScriptGenerator.py @@ -14,6 +14,7 @@ def __init__(self, instrument=None): self.indent = " " self.is_instrument = tk.StringVar() # Initialize the attribute self.is_instrument.set(instrument) + a2setting=A2Setting() def generate_script(self, runs, settings, scriptname): script = f"# Script created by SANSScript at {datetime.now()}\n\n" @@ -158,14 +159,14 @@ def do_sans_run(self, script, exp, indent, settings): return script def aperture_string(self, size): - a2setting = A2Setting() - if size in [a2setting.Large, a2setting.Larmor]: + + if size in [A2setting.Large, A2setting.Larmor]: return "large" - elif size in [a2setting.Medium, a2setting.Larmor]: + elif size in [A2setting.Medium, A2setting.Larmor]: return "medium" - elif size in [a2setting.Small, a2setting.Larmor]: + elif size in [A2setting.Small, A2setting.Larmor]: return "small" - elif size in [a2setting.XLarge, a2setting.Larmor]: + elif size in [A2setting.XLarge, A2setting.Larmor]: return "xlarge" return "" @@ -246,11 +247,11 @@ def get_minutes(wait, length, count_rate): runs = [experiment(position="pos1", trans=10, sans=20, trans_wait="Minutes", sans_wait="Minutes", sample="Sample1", thickness=1.0)] experiment_settings =ExperimentSettings() settings = experiment_settings(sample_width="10", sample_height="10", sample_geometry="Flat", order="AllTrans", num_trans=1, num_sans=1, collection_mode="Events", a2_setting_trans="Large", a2_setting_sans="Large") - estimate=Estimation() - estimation = estimate(count_rate=100, move_time=10) + estimation=Estimation() + estimate = estimation(count_rate=100, move_time=10) instrument = "LARMOR" generator = ScriptGenerator(instrument) script = generator.generate_script(runs, settings, "my_script") print(script) - estimated_time = generator.estimate_run_time(runs, settings, estimation) + estimated_time = generator.estimate_run_time(runs, settings, estimate) print(f"Estimated run time: {estimated_time}") \ No newline at end of file diff --git a/SettingsPanel.py b/SettingsPanel.py index b141465..9ef5eb4 100644 --- a/SettingsPanel.py +++ b/SettingsPanel.py @@ -10,18 +10,18 @@ class SettingsPanel(ttk.Frame): #def __init__(self, parent, experiment=None, inst_info=None): - def __init__(self, parent,inst_info=None): + def __init__(self, parent,inst_info=None, settings=None): super().__init__(parent) self.parent = parent - + self.settings = settings if settings is not None else Settings() self.inst_info = inst_info if inst_info is not None else InstrumentInfo() #self.settings_vm = settings_vm if settings_vm is not None else SettingsPanelVM() #self.instrument_vm = instrument_vm if instrument_vm is not None else InstrumentVM() self.on_preview_clicked = None self.on_write_clicked = None self.property_changed = [] - #self.order_choices = RunOrder.get_choices() - #self.geometry_choices = Geometry.get_choices() + self.order_choices = RunOrder.get_choices() + self.geometry_choices = Geometry.get_choices() #self._experiment = experiment if experiment is not None else ExperimentSettings() #self._inst_info = inst_info if inst_info is not None else InstrumentInfo() @@ -34,8 +34,7 @@ def create_widgets(self): #self.write_button = ttk.Button(self, text="Write Script", command=self.on_write_clicked) #self.write_button.pack(side=tk.LEFT, padx=5, pady=5) - self.settings = Settings() - self.inst_info = InstrumentInfo() + label = ttk.Label(self, text="Settings", font=("Helvetica", 16, "bold")) label.grid(row=0, column=0, columnspan=2, pady=10) From 0e6d6d3817725b12e26b8a0fc6de887c8eb0b1f0 Mon Sep 17 00:00:00 2001 From: Dirk Honecker Date: Tue, 31 Dec 2024 17:43:40 +0000 Subject: [PATCH 18/88] Connect changes of instrument --- MainWindow.py | 21 ++++++++++-------- Models/ExperimentSettings.py | 43 +++++++++++++++++++++++------------- Properties/Settings.ini | 2 +- Properties/Settings.py | 9 ++++---- Scripting/ScriptGenerator.py | 21 +++++++++--------- 5 files changed, 56 insertions(+), 40 deletions(-) diff --git a/MainWindow.py b/MainWindow.py index dbcf52b..f764fd6 100644 --- a/MainWindow.py +++ b/MainWindow.py @@ -92,33 +92,36 @@ def create_widgets(self): instrument_menu = tk.Menu(self.mnu_main, tearoff=0) self.mnu_main.add_cascade(label="Instrument", menu=instrument_menu) - self.instrument_var = tk.StringVar(value="LARMOR") - instrument_menu.add_radiobutton(label="LARMOR", variable=self.instrument_var, value="LARMOR") - instrument_menu.add_radiobutton(label="LOQ", variable=self.instrument_var, value="LOQ") - instrument_menu.add_radiobutton(label="SANS2D", variable=self.instrument_var, value="SANS2D") - instrument_menu.add_radiobutton(label="ZOOM", variable=self.instrument_var, value="ZOOM") + + instrument_menu.add_radiobutton(label="LARMOR", variable=self.settings.experiment.is_instrument, value="LARMOR") + instrument_menu.add_radiobutton(label="LOQ", variable=self.settings.experiment.is_instrument, value="LOQ") + instrument_menu.add_radiobutton(label="SANS2D", variable=self.settings.experiment.is_instrument, value="SANS2D") + instrument_menu.add_radiobutton(label="ZOOM", variable=self.settings.experiment.is_instrument, value="ZOOM") """ def onFrameConfigure(self, event): self.canvas.configure(scrollregion=self.canvas.bbox("all")) """ + + def load_last_instrument(self): inst = self.settings.last_instrument # Replace with actual settings retrieval - self.instrument_var.set(inst) + self.settings.experiment.is_instrument = inst + def pnl_settings_on_write_clicked(self): - filetypes = [("genie_python Files", "*.py")] if self.instrument_var.get() in ["LARMOR", "LOQ", "ZOOM", "SANS2D"] else [("Open Genie Files", "*.gcl")] + filetypes = [("genie_python Files", "*.py")] if self.settings.is_instrument in ["LARMOR", "LOQ", "ZOOM", "SANS2D"] else [("Open Genie Files", "*.gcl")] filepath = filedialog.asksaveasfilename(defaultextension=filetypes[0][1], filetypes=filetypes) if filepath: scriptname = filepath.split("/")[-1].replace(" ", "_").rsplit(".", 1)[0] - script_generator= ScriptGenerator(self.instrument_var.get()) + script_generator= ScriptGenerator(self.settings.is_instrument) script = script_generator.generate_script(self.data.runs, self.settings.experiment, scriptname) with open(filepath, "w") as file: file.write(script) def pnl_settings_on_preview_clicked(self): - self.script_generator= ScriptGenerator(self.instrument_var.get()) + self.script_generator= ScriptGenerator(self.settings.is_instrument) self.script = self.script_generator.generate_script(self.data.runs, self.settings.experiment, "my_script") print(self.script) preview_dialog = PreviewDialog(self, self.script) diff --git a/Models/ExperimentSettings.py b/Models/ExperimentSettings.py index 6aa4e8f..09454fe 100644 --- a/Models/ExperimentSettings.py +++ b/Models/ExperimentSettings.py @@ -27,10 +27,10 @@ def __init__(self, settings=None, estimation=None): self._is_valid = tk.BooleanVar(value=True) self.property_changed = [] - self.is_instrument = tk.StringVar(value=self.settings.last_instrument) + self._is_instrument = tk.StringVar(value=self.settings.last_instrument) + self._is_instrument.trace_add("write", self.on_property_changed) + self._is_instrument.trace_add("write", self.update_instrument) - self.is_instrument.trace_add("write", self.update_instrument) - self.is_instrument.trace_add("write", self.on_property_changed) self._order.trace_add("write", self.on_property_changed) self._num_sans.trace_add("write", self.on_property_changed) self._num_trans.trace_add("write", self.on_property_changed) @@ -54,7 +54,8 @@ def __init__(self, settings=None, estimation=None): self.update_instrument() - def __call__(self, order=None, num_sans=None, num_trans=None, loop_per_run=None, a2_setting_sans=None, a2_setting_trans=None, sample_geometry=None, sample_height=None, sample_width=None, collection_mode=None, is_valid=None): + ''' + def __call__(self, order=None, num_sans=None, num_trans=None, loop_per_run=None, a2_setting_sans=None, a2_setting_trans=None, sample_geometry=None, sample_height=None, sample_width=None, collection_mode=None, is_instrument=None, is_valid=None): if order is not None: self.order = order if num_sans is not None: @@ -77,26 +78,29 @@ def __call__(self, order=None, num_sans=None, num_trans=None, loop_per_run=None, self.collection_mode = collection_mode if is_valid is not None: self.is_valid = is_valid + if is_instrument is not None: + self.is_instrument = is_instrument + ''' def on_property_changed(self, *args): for callback in self.property_changed: callback(args[0]) - self.save() + self.settings.save() def update_instrument(self, *args): - if self.is_instrument.get() == "LARMOR": - self.set_instrument(self.is_instrument.get(), "A1, S1 Setting:", self.larmor_aperture_sizes,"Data Acquisition:",self.larmor_data_collection, 40) - elif self.is_instrument.get() == "LOQ": - self.set_instrument(self.is_instrument.get(), "A2 Setting:", self.loq_aperture_sizes,"Data Acquisition:",self.loq_data_collection, 170) - elif self.is_instrument.get() == "SANS2D": - self.set_instrument(self.is_instrument.get(), "A6 Setting:", self.sans2d_aperture_sizes,"Data Acquisition:",self.sans2d_data_collection, 40) - elif self.is_instrument.get() == "ZOOM": - self.set_instrument(self.is_instrument.get(), "A6 Setting:", self.zoom_aperture_sizes,"Data Acquisition:",self.zoom_data_collection, 40) + if self.is_instrument == "LARMOR": + self.set_instrument(self.is_instrument, "A1, S1 Setting:", self.larmor_aperture_sizes,"Data Acquisition:",self.larmor_data_collection, 40) + elif self.is_instrument == "LOQ": + self.set_instrument(self.is_instrument, "A2 Setting:", self.loq_aperture_sizes,"Data Acquisition:",self.loq_data_collection, 170) + elif self.is_instrument == "SANS2D": + self.set_instrument(self.is_instrument, "A6 Setting:", self.sans2d_aperture_sizes,"Data Acquisition:",self.sans2d_data_collection, 40) + elif self.is_instrument == "ZOOM": + self.set_instrument(self.is_instrument, "A6 Setting:", self.zoom_aperture_sizes,"Data Acquisition:",self.zoom_data_collection, 40) def set_instrument(self, instrument, a2_label, a2_choices, data_collection_label, data_collection_choices, count_rate): - self.is_instrument.set(instrument) + self.settings.is_instrument = instrument self.a2_label = a2_label self.a2_choices = a2_choices @@ -106,7 +110,7 @@ def set_instrument(self, instrument, a2_label, a2_choices, data_collection_label self.collection_mode_enabled = True self.estimation.count_rate = count_rate - + ''' def save(self): self.settings.run_order = self.order @@ -120,6 +124,7 @@ def save(self): self.settings.sample_width = self.sample_width self.settings.collection_mode = self.collection_mode self.settings.save() + ''' @property def order(self): @@ -213,6 +218,14 @@ def collection_mode(self): @collection_mode.setter def collection_mode(self, value): self._collection_mode.set(value) + + @property + def is_instrument(self): + return self._is_instrument.get() + + @is_instrument.setter + def is_instrument(self, value): + self._is_instrument.set(value) @property def is_valid(self): diff --git a/Properties/Settings.ini b/Properties/Settings.ini index 51421f9..9834325 100644 --- a/Properties/Settings.ini +++ b/Properties/Settings.ini @@ -1,5 +1,5 @@ [DEFAULT] -lastinstrument = LOQ +lastinstrument = LARMOR sampleheight = 10.0 samplewidth = 10.0 geometry = Flat Plate diff --git a/Properties/Settings.py b/Properties/Settings.py index 2b2e2eb..1f80c4d 100644 --- a/Properties/Settings.py +++ b/Properties/Settings.py @@ -132,7 +132,8 @@ def save(self): self.dict_settings.write(configfile) # Example usage -settings = Settings() -print(settings.last_instrument) -settings.last_instrument = "LOQ" -print(settings.last_instrument) \ No newline at end of file +if __name__ == "__main__": + settings = Settings() + print(settings.last_instrument) + settings.last_instrument = "LOQ" + print(settings.last_instrument) \ No newline at end of file diff --git a/Scripting/ScriptGenerator.py b/Scripting/ScriptGenerator.py index 963df75..5a4a102 100644 --- a/Scripting/ScriptGenerator.py +++ b/Scripting/ScriptGenerator.py @@ -12,9 +12,8 @@ class ScriptGenerator: def __init__(self, instrument=None): self.indent = " " - self.is_instrument = tk.StringVar() # Initialize the attribute - self.is_instrument.set(instrument) - a2setting=A2Setting() + self.instrument = instrument + self.a2setting=A2Setting() def generate_script(self, runs, settings, scriptname): script = f"# Script created by SANSScript at {datetime.now()}\n\n" @@ -32,13 +31,13 @@ def generate_header(self, scriptname): script += "from genie_python import genie as g, BLOCK_NAMES as b\n" script += "sys.path.append(r\"c:\\instrument\\scripts\")\n" - if self.is_instrument.get() == "LARMOR": + if self.instrument == "LARMOR": script += "from instrument.larmor import * # pylint: disable=wildcard-import, unused-wildcard-import\n" - if self.is_instrument.get() == "LOQ": + if self.instrument == "LOQ": script += "from instrument.loq import * # pylint: disable=wildcard-import, unused-wildcard-import\n" - if self.is_instrument.get() == "ZOOM": + if self.instrument == "ZOOM": script += "from instrument.zoom import * # pylint: disable=wildcard-import, unused-wildcard-import\n" - if self.is_instrument.get() == "SANS2D": + if self.instrument == "SANS2D": script += "from instrument.sans2d import * # pylint: disable=wildcard-import, unused-wildcard-import\n" script += f"\ndef {scriptname}():\n" @@ -53,7 +52,7 @@ def generate_setup(self, settings): def check_positions(self, runs): script = "" - if self.is_instrument.get() == "LARMOR": + if self.instrument == "LARMOR": position_list = ",".join([exp.position for exp in runs if exp.position]) if position_list: script += self.get_indent_string(1) + f"if check_move_pos(['{position_list}']) != True:\n" @@ -123,9 +122,9 @@ def do_trans_run(self, script, exp, indent, settings): waitfor = "seconds" elif exp.trans_wait == "Minutes": waitfor = "minutes" - if self.is_instrument.get() == "LARMOR" or self.is_instrument.get() == "ZOOM": + if self.instrument == "LARMOR" or self.instrument == "ZOOM": script += self.get_indent_string(indent) + f"do_trans(title='{fulltitle}', pos='{exp.position}', {waitfor}={exp.trans}, thickness={exp.thickness})\n" - elif self.is_instrument.get() == "LOQ": + elif self.instrument == "LOQ": script += self.get_indent_string(indent) + f"do_trans(title='{fulltitle}', pos='{exp.position}', {waitfor}={exp.trans}, thickness={exp.thickness}, aperture='small')\n" else: script += self.get_indent_string(indent) + f"do_trans(title='{fulltitle}', pos='{exp.position}', {waitfor}={exp.trans}, thickness={exp.thickness}, dae='transmission', aperture='{self.aperture_string(settings.a2_setting_trans)}')\n" @@ -151,7 +150,7 @@ def do_sans_run(self, script, exp, indent, settings): waitfor = "seconds" elif exp.sans_wait == "Minutes": waitfor = "minutes" - if self.is_instrument.get() == "LARMOR" or self.is_instrument.get() == "ZOOM": + if self.instrument == "LARMOR" or self.instrument == "ZOOM": script += self.get_indent_string(indent) + f"do_sans(title='{fulltitle}', pos='{exp.position}', {waitfor}={exp.sans}, thickness={exp.thickness}, dae='{rtype}')\n" else: script += self.get_indent_string(indent) + f"do_sans(title='{fulltitle}', pos='{exp.position}', {waitfor}={exp.sans}, thickness={exp.thickness}, dae='{rtype}', aperture='{self.aperture_string(settings.a2_setting_sans)}')\n" From a99b7329b2a954c266d813721d60dccaa90570a8 Mon Sep 17 00:00:00 2001 From: Dirk Honecker Date: Thu, 2 Jan 2025 03:24:56 +0000 Subject: [PATCH 19/88] Update SettingsPanel when instrument changes --- MainWindow.py | 15 +++++++++++---- Models/ExperimentSettings.py | 10 +++++----- 2 files changed, 16 insertions(+), 9 deletions(-) diff --git a/MainWindow.py b/MainWindow.py index f764fd6..428e802 100644 --- a/MainWindow.py +++ b/MainWindow.py @@ -93,15 +93,22 @@ def create_widgets(self): instrument_menu = tk.Menu(self.mnu_main, tearoff=0) self.mnu_main.add_cascade(label="Instrument", menu=instrument_menu) - instrument_menu.add_radiobutton(label="LARMOR", variable=self.settings.experiment.is_instrument, value="LARMOR") - instrument_menu.add_radiobutton(label="LOQ", variable=self.settings.experiment.is_instrument, value="LOQ") - instrument_menu.add_radiobutton(label="SANS2D", variable=self.settings.experiment.is_instrument, value="SANS2D") - instrument_menu.add_radiobutton(label="ZOOM", variable=self.settings.experiment.is_instrument, value="ZOOM") + instrument_menu.add_radiobutton(label="LARMOR", variable=self.settings.experiment.instrument, value="LARMOR", command=self.update_settings_panel) + instrument_menu.add_radiobutton(label="LOQ", variable=self.settings.experiment.instrument, value="LOQ", command=self.update_settings_panel) + instrument_menu.add_radiobutton(label="SANS2D", variable=self.settings.experiment.instrument, value="SANS2D", command=self.update_settings_panel) + instrument_menu.add_radiobutton(label="ZOOM", variable=self.settings.experiment.instrument, value="ZOOM", command=self.update_settings_panel) """ def onFrameConfigure(self, event): self.canvas.configure(scrollregion=self.canvas.bbox("all")) """ + def update_settings_panel(self): + self.pnl_settings.destroy() + self.inst_info = InstrumentInfo(settings=self.settings) + self.pnl_settings = SettingsPanel(self, inst_info=self.inst_info, settings=self.settings) + self.pnl_settings.grid(row=0, column=0, sticky="ew") + self.pnl_settings.on_preview_clicked = self.pnl_settings_on_preview_clicked + self.pnl_settings.on_write_clicked = self.pnl_settings_on_write_clicked def load_last_instrument(self): inst = self.settings.last_instrument # Replace with actual settings retrieval diff --git a/Models/ExperimentSettings.py b/Models/ExperimentSettings.py index 09454fe..4b1de4e 100644 --- a/Models/ExperimentSettings.py +++ b/Models/ExperimentSettings.py @@ -27,9 +27,9 @@ def __init__(self, settings=None, estimation=None): self._is_valid = tk.BooleanVar(value=True) self.property_changed = [] - self._is_instrument = tk.StringVar(value=self.settings.last_instrument) - self._is_instrument.trace_add("write", self.on_property_changed) - self._is_instrument.trace_add("write", self.update_instrument) + self.instrument = tk.StringVar(value=self.settings.last_instrument) + self.instrument.trace_add("write", self.on_property_changed) + self.instrument.trace_add("write", self.update_instrument) self._order.trace_add("write", self.on_property_changed) self._num_sans.trace_add("write", self.on_property_changed) @@ -221,11 +221,11 @@ def collection_mode(self, value): @property def is_instrument(self): - return self._is_instrument.get() + return self.instrument.get() @is_instrument.setter def is_instrument(self, value): - self._is_instrument.set(value) + self.instrument.set(value) @property def is_valid(self): From af6e9838314298f1bcf8f6189136617a2188d6c9 Mon Sep 17 00:00:00 2001 From: Dirk Honecker Date: Thu, 2 Jan 2025 05:34:37 +0000 Subject: [PATCH 20/88] Create list of experiments from table entries --- .gitignore | 6 +- MainWindow.py | 27 ++-- Models/Experiment.py | 5 +- Models/RunOrder.py | 8 +- Properties/Settings.ini | 2 +- Scripting/ScriptGenerator.py | 18 +-- TableDataConstructor.py | 96 ++++++++++--- TableTest.py | 18 +-- TextFile2.py | 17 ++- Transcript_GitCopilot.txt | 266 +++++++++++++++++------------------ 10 files changed, 270 insertions(+), 193 deletions(-) diff --git a/.gitignore b/.gitignore index 9491a2f..41e084b 100644 --- a/.gitignore +++ b/.gitignore @@ -35,7 +35,7 @@ bld/ # Visual Studio 2015/2017 cache/options directory .vs/ -# Uncomment if you have tasks that create the project's static files in wwwroot +# Uncomment if you have tasks that create the project"s static files in wwwroot #wwwroot/ # Visual Studio 2017 auto generated files @@ -202,7 +202,7 @@ PublishScripts/ !**/[Pp]ackages/build/ # Uncomment if necessary however generally it will be regenerated when needed #!**/[Pp]ackages/repositories.config -# NuGet v3's project.json files produces more ignorable files +# NuGet v3"s project.json files produces more ignorable files *.nuget.props *.nuget.targets @@ -323,7 +323,7 @@ __pycache__/ # Tabs Studio *.tss -# Telerik's JustMock configuration file +# Telerik"s JustMock configuration file *.jmconfig # BizTalk build output diff --git a/MainWindow.py b/MainWindow.py index 428e802..b20dac8 100644 --- a/MainWindow.py +++ b/MainWindow.py @@ -16,6 +16,7 @@ from Properties.Settings import Settings from Scripting.ScriptGenerator import ScriptGenerator from Models.ExperimentSettings import ExperimentSettings +from TableDataConstructor import TableDataConstructor TableEntries = "POSITION, TRANS, TRANS_WAIT, SANS, SANS_WAIT, PERIOD, SAMPLE_IDENTIFICATION_STRING, BACKGROUND, THICKNESS, TEMPERATURE, TEMPERATURE2, FIELD, SHEAR_RATE_1, SHEAR_RATE_2, SHEAR_ANGLE_1, SHEAR_ANGLE_2, PRE_COMMAND, POST_COMMAND, RB_NUMBER" @@ -27,7 +28,7 @@ def __init__(self,parent): self.parent.title("SANS Script GUI") self.parent.geometry("800x600") - self.data = DataGridVM() + #self.data = DataGridVM() self.settings = Settings() self.estimation = Estimation() @@ -120,25 +121,31 @@ def load_last_instrument(self): def pnl_settings_on_write_clicked(self): filetypes = [("genie_python Files", "*.py")] if self.settings.is_instrument in ["LARMOR", "LOQ", "ZOOM", "SANS2D"] else [("Open Genie Files", "*.gcl")] filepath = filedialog.asksaveasfilename(defaultextension=filetypes[0][1], filetypes=filetypes) + constructor = TableDataConstructor(TableEntries) + self.data = constructor.create_experiments(self.data_frame.data.values()) if filepath: scriptname = filepath.split("/")[-1].replace(" ", "_").rsplit(".", 1)[0] script_generator= ScriptGenerator(self.settings.is_instrument) - script = script_generator.generate_script(self.data.runs, self.settings.experiment, scriptname) + script = script_generator.generate_script(self.data, self.settings.experiment, scriptname) with open(filepath, "w") as file: file.write(script) def pnl_settings_on_preview_clicked(self): self.script_generator= ScriptGenerator(self.settings.is_instrument) - self.script = self.script_generator.generate_script(self.data.runs, self.settings.experiment, "my_script") + constructor = TableDataConstructor(TableEntries) + self.data = constructor.create_experiments(self.data_frame.data.values()) + self.script = self.script_generator.generate_script(self.data, self.settings.experiment, "my_script") print(self.script) preview_dialog = PreviewDialog(self, self.script) preview_dialog.show() def pnl_estimate_on_calculate_clicked(self): + constructor = TableDataConstructor(TableEntries) + self.data = constructor.create_experiments(self.data_frame.data.values()) self.estimation.script_time = self.script_generator.estimate_run_time(self.data.runs, self.settings.experiment, self.estimation) def btn_create_new_table_click(self): - self.data.runs.clear() + self.data.clear() def btn_open_excel_click(self): filepath = filedialog.askopenfilename(defaultextension=".xlsx", filetypes=[("Excel Files", "*.xls;*.xlsx")]) @@ -147,7 +154,7 @@ def btn_open_excel_click(self): excel = ExcelIO() excel.open_excel_workbook(filepath) exps = excel.extract_experiments() - self.data.runs = exps + self.data = exps except Exception as err: messagebox.showerror("SANS Script - Error", "Could not open Excel spreadsheet") @@ -156,14 +163,18 @@ def mnu_exit_click(self): def mnu_export_to_excel_click(self): excel = ExcelIO() - filepath = filedialog.asksaveasfilename(defaultextension=".xls", filetypes=[("Exel File", "*.xls")]) - excel.export_data_to_excel(filepath,self.data.runs) + filepath = filedialog.asksaveasfilename(defaultextension=".xls", filetypes=[("Exel File", "*.xls")]) + constructor = TableDataConstructor(TableEntries) + self.data = constructor.create_experiments(self.data_frame.data.values()) + excel.export_data_to_excel(filepath,self.runs) def mnu_save_csv_click(self): filepath = filedialog.asksaveasfilename(defaultextension=".csv", filetypes=[("CSV File", "*.csv")]) + constructor = TableDataConstructor(TableEntries) + self.data = constructor.create_experiments(self.data_frame.data.values()) if filepath: try: - CsvExporter.export(filepath, self.data.runs) + CsvExporter.export(filepath, self.data) except Exception as err: messagebox.showerror("SANS Script - Error", "Could not save as CSV") diff --git a/Models/Experiment.py b/Models/Experiment.py index 1d5dbac..cc33835 100644 --- a/Models/Experiment.py +++ b/Models/Experiment.py @@ -114,7 +114,10 @@ def sample(self, value): @property def thickness(self): - return self._thickness.get() + value = self._thickness.get() + if value == "": + raise ValueError("Thickness cannot be an empty string") + return float(value) @thickness.setter def thickness(self, value): diff --git a/Models/RunOrder.py b/Models/RunOrder.py index ea95720..ca5d07d 100644 --- a/Models/RunOrder.py +++ b/Models/RunOrder.py @@ -1,8 +1,8 @@ class RunOrder: - ALL_TRANS = "All TRANS first" - TRANS_FIRST = "Alternate - TRANS first" - ALL_SANS = "All SANS first" - SANS_FIRST = "Alternate - SANS first" + ALL_TRANS = [1, "All TRANS first"] + TRANS_FIRST = [2, "Alternate - TRANS first"] + ALL_SANS = [3, "All SANS first"] + SANS_FIRST = [4, "Alternate - SANS first"] choices = ( ALL_TRANS, diff --git a/Properties/Settings.ini b/Properties/Settings.ini index 9834325..ba14c99 100644 --- a/Properties/Settings.ini +++ b/Properties/Settings.ini @@ -3,7 +3,7 @@ lastinstrument = LARMOR sampleheight = 10.0 samplewidth = 10.0 geometry = Flat Plate -runorder = All Trans First +runorder = 1 numtrans = 1 numsans = 1 loopperrun = False diff --git a/Scripting/ScriptGenerator.py b/Scripting/ScriptGenerator.py index 5a4a102..c4891c2 100644 --- a/Scripting/ScriptGenerator.py +++ b/Scripting/ScriptGenerator.py @@ -7,13 +7,13 @@ from Models.A2Setting import A2Setting from Models.RunOrder import RunOrder from Models.DataCollection import DataCollection -from Models.A2Setting import A2Setting + class ScriptGenerator: def __init__(self, instrument=None): self.indent = " " self.instrument = instrument - self.a2setting=A2Setting() + def generate_script(self, runs, settings, scriptname): script = f"# Script created by SANSScript at {datetime.now()}\n\n" @@ -65,7 +65,7 @@ def generate_runs(self, runs, settings): if not runs: return script - if settings.order == RunOrder.ALL_TRANS: + if float(settings.order) == RunOrder.ALL_TRANS[0]: script += self.get_indent_string(1) + f"for i in range({settings.num_trans}):\n" for exp in runs: if exp.trans: @@ -74,7 +74,7 @@ def generate_runs(self, runs, settings): for exp in runs: if exp.sans: script = self.do_sans_run(script, exp, 2, settings) - elif settings.order == RunOrder.ALL_SANS: + elif float(settings.order) == RunOrder.ALL_SANS[0]: script += self.get_indent_string(1) + f"for i in range({settings.num_sans}):\n" for exp in runs: if exp.sans: @@ -91,7 +91,7 @@ def generate_runs(self, runs, settings): script += self.get_indent_string(1) + "while True:\n" indent = 2 for exp in runs: - if settings.order == RunOrder.TRANS_FIRST: + if float(settings.order) == RunOrder.TRANS_FIRST[0]: if exp.trans: script += self.get_indent_string(indent) + "if count < num_trans:\n" script = self.do_trans_run(script, exp, indent + 1, settings) @@ -159,13 +159,13 @@ def do_sans_run(self, script, exp, indent, settings): def aperture_string(self, size): - if size in [A2setting.Large, A2setting.Larmor]: + if size in [A2Setting.Large, A2Setting.Larmor]: return "large" - elif size in [A2setting.Medium, A2setting.Larmor]: + elif size in [A2Setting.Medium, A2Setting.Larmor]: return "medium" - elif size in [A2setting.Small, A2setting.Larmor]: + elif size in [A2Setting.Small, A2Setting.Larmor]: return "small" - elif size in [A2setting.XLarge, A2setting.Larmor]: + elif size in [A2Setting.XLarge, A2Setting.Larmor]: return "xlarge" return "" diff --git a/TableDataConstructor.py b/TableDataConstructor.py index 9f04b99..d6bc8b0 100644 --- a/TableDataConstructor.py +++ b/TableDataConstructor.py @@ -1,3 +1,6 @@ +import tkinter as tk +from Models.Experiment import Experiment + class TableDataConstructor: def __init__(self, table_entries): self.table_entries = table_entries.split(", ") @@ -21,26 +24,73 @@ def deconstruct_data(self, data_dict): data_list.append(row) return data_list -# Example usage -TableEntries = "POSITION, TRANS, TRANS_WAIT, SANS, SANS_WAIT, PERIOD, SAMPLE_IDENTIFICATION_STRING, BACKGROUND, THICKNESS, TEMPERATURE, TEMPERATURE2, FIELD, SHEAR_RATE_1, SHEAR_RATE_2, SHEAR_ANGLE_1, SHEAR_ANGLE_2, PRE_COMMAND, POST_COMMAND, RB_NUMBER" -constructor = TableDataConstructor(TableEntries) -empty_data = constructor.construct_empty_data() -print(empty_data) - -# Example usage -TableEntries = "col1, col2, label, col3, col4, label2, col5, col6, label3" -constructor = TableDataConstructor(TableEntries) - -# Construct data from a list of lists -data_list = [ - [99.88, 108.79, "rec1", 99.88, 108.79, "rec1", 99.88, 108.79, "rec1"], - [99.88, 108.79, "rec2", 99.88, 108.79, "rec2", 99.88, 108.79, "rec2"], - [99.88, 108.79, "rec3", 99.88, 108.79, "rec3", 99.88, 108.79, "rec3"]] - # Add more rows as needed - -data = constructor.construct_data(data_list) -print("Data:", data) - -# Deconstruct data back to list of lists -deconstructed_data_list = constructor.deconstruct_data(data) -print("Deconstructed Data List:", deconstructed_data_list) + def create_experiments(self, data_list): + experiments = [] + for row in data_list: + experiment = Experiment( + position=row.get("POSITION", ""), + trans=row.get("TRANS", 0), + trans_wait=row.get("TRANS_WAIT", ""), + sans=row.get("SANS", 0), + sans_wait=row.get("SANS_WAIT", ""), + period=row.get("PERIOD", ""), + background=row.get("BACKGROUND", ""), + sample=row.get("SAMPLE_IDENTIFICATION_STRING", ""), + thickness=row.get("THICKNESS", 0), + temperature1=row.get("TEMPERATURE", ""), + temperature2=row.get("TEMPERATURE2", ""), + field=row.get("FIELD", ""), + shear_rate1=row.get("SHEAR_RATE_1", ""), + shear_rate2=row.get("SHEAR_RATE_2", ""), + shear_angle1=row.get("SHEAR_ANGLE_1", ""), + shear_angle2=row.get("SHEAR_ANGLE_2", ""), + pre_command=row.get("PRE_COMMAND", ""), + post_command=row.get("POST_COMMAND", ""), + rb_number=row.get("RB_NUMBER", "") + ) + experiments.append(experiment) + return experiments + +if __name__ == "__main__": + root = tk.Tk() + # Example usage + TableEntries = "POSITION, TRANS, TRANS_WAIT, SANS, SANS_WAIT, PERIOD, SAMPLE_IDENTIFICATION_STRING, BACKGROUND, THICKNESS, TEMPERATURE, TEMPERATURE2, FIELD, SHEAR_RATE_1, SHEAR_RATE_2, SHEAR_ANGLE_1, SHEAR_ANGLE_2, PRE_COMMAND, POST_COMMAND, RB_NUMBER" + constructor = TableDataConstructor(TableEntries) + empty_data = constructor.construct_empty_data() + print(empty_data) + + # Example usage + TableEntries = "col1, col2, label, col3, col4, label2, col5, col6, label3" + constructor = TableDataConstructor(TableEntries) + + # Construct data from a list of lists + data_list = [ + [99.88, 108.79, "rec1", 99.88, 108.79, "rec1", 99.88, 108.79, "rec1"], + [99.88, 108.79, "rec2", 99.88, 108.79, "rec2", 99.88, 108.79, "rec2"], + [99.88, 108.79, "rec3", 99.88, 108.79, "rec3", 99.88, 108.79, "rec3"]] + # Add more rows as needed + + data = constructor.construct_data(data_list) + print("Data:", data) + + # Deconstruct data back to list of lists + deconstructed_data_list = constructor.deconstruct_data(data) + print("Deconstructed Data List:", deconstructed_data_list) + + # Example usage + TableEntries = "POSITION, TRANS, TRANS_WAIT, SANS, SANS_WAIT, PERIOD, SAMPLE_IDENTIFICATION_STRING, BACKGROUND, THICKNESS, TEMPERATURE, TEMPERATURE2, FIELD, SHEAR_RATE_1, SHEAR_RATE_2, SHEAR_ANGLE_1, SHEAR_ANGLE_2, PRE_COMMAND, POST_COMMAND, RB_NUMBER" + constructor = TableDataConstructor(TableEntries) + + # Construct data from a list of lists + data_list = [ + ["Pos1", 10.5, "Wait1", 20.0, "Wait2", "Period1", "Sample1", "Background1", 1.0, "25", "30", "1.5T", "100", "200", "45", "90", "Pre Command1", "Post Command1", "RB12345"], + ["Pos2", 15.0, "Wait3", 25.0, "Wait4", "Period2", "Sample2", "Background2", 2.0, "35", "40", "2.0T", "150", "250", "50", "95", "Pre Command2", "Post Command2", "RB67890"] + ] + + data = constructor.construct_data(data_list) + print("Data:", data) + + # Create experiments from the constructed data + experiments = constructor.create_experiments(data.values()) + for exp in experiments: + print(exp.position, exp.trans, exp.sans, exp.temperature1, exp.rb_number) diff --git a/TableTest.py b/TableTest.py index 6e6d202..ad7ec38 100644 --- a/TableTest.py +++ b/TableTest.py @@ -1,5 +1,5 @@ -''' +""" import tkinter as tk class SimpleTableInput(tk.Frame): @@ -27,7 +27,7 @@ def __init__(self, parent, rows, columns): self.grid_rowconfigure(rows, weight=1) def get(self): - '''Return a list of lists, containing the data in the table''' + """Return a list of lists, containing the data in the table""" result = [] for row in range(self.rows): current_row = [] @@ -38,10 +38,10 @@ def get(self): return result def _validate(self, P): - '''Perform input validation. + """Perform input validation. Allow only an empty value, or a value that can be converted to a float - ''' + """ if P.strip() == "": return True @@ -67,10 +67,10 @@ def on_submit(self): root = tk.Tk() Example(root).pack(side="top", fill="both", expand=True) root.mainloop() -''' +""" -''' +""" move to next column and row with enter and tab import tkinter as tk @@ -94,8 +94,8 @@ def __init__(self): def handle_enter(self, event): # get the row and column of the entry that got the event entry = event.widget - row = int(entry.grid_info()['row']) - column = int(entry.grid_info()['column']) + row = int(entry.grid_info()["row"]) + column = int(entry.grid_info()["column"]) # compute the new row; either the next row or circle # back around to the first row @@ -107,5 +107,5 @@ def handle_enter(self, event): example = Example() tk.mainloop() -''' +""" diff --git a/TextFile2.py b/TextFile2.py index d64e6a0..100c904 100644 --- a/TextFile2.py +++ b/TextFile2.py @@ -4,8 +4,8 @@ import pandas as pd from Models.DataCollection import DataCollection from ViewModels.DataGridVM import DataGridVM -from ViewModels.SettingsPanelVM import SettingsPanelVM -from ViewModels.InstrumentVM import InstrumentVM +#from ViewModels.SettingsPanelVM import SettingsPanelVM +#from ViewModels.InstrumentVM import InstrumentVM from Models.InstrumentInfo import InstrumentInfo from Models.Estimation import Estimation from Models.ExperimentSettings import ExperimentSettings @@ -24,5 +24,18 @@ from Models.ExperimentSettings import ExperimentSettings from Models.A2Setting import A2Setting +if __name__ == "__main__": + root = tk.Tk() + runs = [Experiment(position="pos1", trans=10, sans=20, trans_wait="Minutes", sans_wait="Minutes", sample="Sample1", thickness=1.0)] + settings = ExperimentSettings() + settings(sample_width=10, sample_height=10, sample_geometry="Flat ", order="AllTrans", num_trans=1, num_sans=1, collection_mode="Events", a2_setting_trans="Large", a2_setting_sans="Large") + + estimation = Estimation(count_rate=100, move_time=10) + instrument = "LARMOR" + generator = ScriptGenerator(instrument) + script = generator.generate_script(runs, settings, "my_script") + print(script) + estimated_time = generator.estimate_run_time(runs, settings, estimation) + print(f"Estimated run time: {estimated_time}") diff --git a/Transcript_GitCopilot.txt b/Transcript_GitCopilot.txt index 17862f6..f0585ac 100644 --- a/Transcript_GitCopilot.txt +++ b/Transcript_GitCopilot.txt @@ -272,11 +272,11 @@ import xml.etree.ElementTree as ET root = ET.Element("Project", ToolsVersion="12.0", DefaultTargets="Build", xmlns="http://schemas.microsoft.com/developer/msbuild/2003") -ET.SubElement(root, "Import", Project="$(MSBuildExtensionsPath)\\$(MSBuildToolsVersion)\\Microsoft.Common.props", Condition="Exists('$(MSBuildExtensionsPath)\\$(MSBuildToolsVersion)\\Microsoft.Common.props')") +ET.SubElement(root, "Import", Project="$(MSBuildExtensionsPath)\\$(MSBuildToolsVersion)\\Microsoft.Common.props", Condition="Exists("$(MSBuildExtensionsPath)\\$(MSBuildToolsVersion)\\Microsoft.Common.props")") property_group = ET.SubElement(root, "PropertyGroup") -ET.SubElement(property_group, "Configuration", Condition=" '$(Configuration)' == '' ").text = "Debug" -ET.SubElement(property_group, "Platform", Condition=" '$(Platform)' == '' ").text = "AnyCPU" +ET.SubElement(property_group, "Configuration", Condition=" "$(Configuration)" == "" ").text = "Debug" +ET.SubElement(property_group, "Platform", Condition=" "$(Platform)" == "" ").text = "AnyCPU" ET.SubElement(property_group, "ProjectGuid").text = "{5DBFF2C3-05A0-4B4B-9080-8224D86A7B40}" ET.SubElement(property_group, "OutputType").text = "WinExe" ET.SubElement(property_group, "AppDesignerFolder").text = "Properties" @@ -303,7 +303,7 @@ ET.SubElement(property_group, "ApplicationVersion").text = "1.0.0.%2a" ET.SubElement(property_group, "UseApplicationTrust").text = "false" ET.SubElement(property_group, "BootstrapperEnabled").text = "true" -debug_property_group = ET.SubElement(root, "PropertyGroup", Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ") +debug_property_group = ET.SubElement(root, "PropertyGroup", Condition=" "$(Configuration)|$(Platform)" == "Debug|AnyCPU" ") ET.SubElement(debug_property_group, "PlatformTarget").text = "x64" ET.SubElement(debug_property_group, "DebugSymbols").text = "true" ET.SubElement(debug_property_group, "DebugType").text = "full" @@ -314,7 +314,7 @@ ET.SubElement(debug_property_group, "ErrorReport").text = "prompt" ET.SubElement(debug_property_group, "WarningLevel").text = "4" ET.SubElement(debug_property_group, "Prefer32Bit").text = "false" -release_property_group = ET.SubElement(root, "PropertyGroup", Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ") +release_property_group = ET.SubElement(root, "PropertyGroup", Condition=" "$(Configuration)|$(Platform)" == "Release|AnyCPU" ") ET.SubElement(release_property_group, "PlatformTarget").text = "x64" ET.SubElement(release_property_group, "DebugType").text = "pdbonly" ET.SubElement(release_property_group, "Optimize").text = "true" @@ -520,9 +520,9 @@ class MainWindow(tk.Tk): filetypes = [("genie_python Files", "*.py")] if self.instrument.is_larmor or self.instrument.is_loq or self.instrument.is_zoom or self.instrument.is_sans2d else [("Open Genie Files", "*.gcl")] filepath = filedialog.asksaveasfilename(defaultextension=filetypes[0][1], filetypes=filetypes) if filepath: - scriptname = filepath.split('/')[-1].replace(" ", "_").rsplit('.', 1)[0] + scriptname = filepath.split("/")[-1].replace(" ", "_").rsplit(".", 1)[0] script = self.inst_info.script_generator.generate_script(self.data.runs, self.settings.experiment, scriptname) - with open(filepath, 'w') as file: + with open(filepath, "w") as file: file.write(script) def pnl_settings_on_preview_clicked(self): @@ -975,7 +975,7 @@ class EstimationPanel(ttk.Frame): ttk.Label(grid_frame, text="Est. script time:").grid(row=2, column=0, sticky=tk.E) script_time_frame = ttk.Frame(grid_frame) script_time_frame.grid(row=2, column=1, columnspan=2) - self.txt_estimate = ttk.Entry(script_time_frame, width=15, textvariable=self.view_model.script_time, state='readonly') + self.txt_estimate = ttk.Entry(script_time_frame, width=15, textvariable=self.view_model.script_time, state="readonly") self.txt_estimate.pack(side=tk.LEFT, padx=5, pady=5) # Calculate button @@ -1084,21 +1084,21 @@ import configparser config = configparser.ConfigParser() -config['DEFAULT'] = { - 'LastInstrument': 'LARMOR', - 'SampleHeight': '11', - 'SampleWidth': '11', - 'Geometry': 'Flat Plate', - 'RunOrder': 'Alternate - TRANS first', - 'NumTrans': '1', - 'NumSans': '1', - 'LoopPerRun': 'False', - 'A2SettingSans': 'Large = 25, 16 mm', - 'A2SettingTrans': 'Large = 25, 16 mm', - 'CollectionMode': 'Events' +config["DEFAULT"] = { + "LastInstrument": "LARMOR", + "SampleHeight": "11", + "SampleWidth": "11", + "Geometry": "Flat Plate", + "RunOrder": "Alternate - TRANS first", + "NumTrans": "1", + "NumSans": "1", + "LoopPerRun": "False", + "A2SettingSans": "Large = 25, 16 mm", + "A2SettingTrans": "Large = 25, 16 mm", + "CollectionMode": "Events" } -with open('app_config.ini', 'w') as configfile: +with open("app_config.ini", "w") as configfile: config.write(configfile) ``` @@ -1557,9 +1557,9 @@ class GeniePythonScriptGenerator: def generate_setup(self, settings): script = "" - script += self.get_indent_string(1) + f"g.change_sample_par('Width', '{settings.sample_width}')\n" - script += self.get_indent_string(1) + f"g.change_sample_par('height', '{settings.sample_height}')\n" - script += self.get_indent_string(1) + f"g.change_sample_par('Geometry', '{settings.sample_geometry}')\n\n" + script += self.get_indent_string(1) + f"g.change_sample_par("Width", "{settings.sample_width}")\n" + script += self.get_indent_string(1) + f"g.change_sample_par("height", "{settings.sample_height}")\n" + script += self.get_indent_string(1) + f"g.change_sample_par("Geometry", "{settings.sample_geometry}")\n\n" return script def check_positions(self, runs): @@ -1567,8 +1567,8 @@ class GeniePythonScriptGenerator: if self.is_larmor: position_list = ",".join([exp.position for exp in runs if exp.position]) if position_list: - script += self.get_indent_string(1) + f"if check_move_pos(['{position_list}']) != True:\n" - script += self.get_indent_string(2) + "print('Invalid positions - script aborted')\n" + script += self.get_indent_string(1) + f"if check_move_pos(["{position_list}"]) != True:\n" + script += self.get_indent_string(2) + "print("Invalid positions - script aborted")\n" script += self.get_indent_string(2) + "return\n\n" return script @@ -1635,11 +1635,11 @@ class GeniePythonScriptGenerator: elif exp.trans_wait == "Minutes": waitfor = "minutes" if self.is_larmor or self.is_zoom: - script += self.get_indent_string(indent) + f"do_trans(title='{fulltitle}', pos='{exp.position}', {waitfor}={exp.trans}, thickness={exp.thickness})\n" + script += self.get_indent_string(indent) + f"do_trans(title="{fulltitle}", pos="{exp.position}", {waitfor}={exp.trans}, thickness={exp.thickness})\n" elif self.is_loq: - script += self.get_indent_string(indent) + f"do_trans(title='{fulltitle}', pos='{exp.position}', {waitfor}={exp.trans}, thickness={exp.thickness}, aperture='small')\n" + script += self.get_indent_string(indent) + f"do_trans(title="{fulltitle}", pos="{exp.position}", {waitfor}={exp.trans}, thickness={exp.thickness}, aperture="small")\n" else: - script += self.get_indent_string(indent) + f"do_trans(title='{fulltitle}', pos='{exp.position}', {waitfor}={exp.trans}, thickness={exp.thickness}, dae='transmission', aperture='{self.aperture_string(settings.a2_setting_trans)}')\n" + script += self.get_indent_string(indent) + f"do_trans(title="{fulltitle}", pos="{exp.position}", {waitfor}={exp.trans}, thickness={exp.thickness}, dae="transmission", aperture="{self.aperture_string(settings.a2_setting_trans)}")\n" script += self.get_indent_string(indent) + exp.post_command + "\n" return script @@ -1663,9 +1663,9 @@ class GeniePythonScriptGenerator: elif exp.sans_wait == "Minutes": waitfor = "minutes" if self.is_larmor or self.is_zoom: - script += self.get_indent_string(indent) + f"do_sans(title='{fulltitle}', pos='{exp.position}', {waitfor}={exp.sans}, thickness={exp.thickness}, dae='{rtype}')\n" + script += self.get_indent_string(indent) + f"do_sans(title="{fulltitle}", pos="{exp.position}", {waitfor}={exp.sans}, thickness={exp.thickness}, dae="{rtype}")\n" else: - script += self.get_indent_string(indent) + f"do_sans(title='{fulltitle}', pos='{exp.position}', {waitfor}={exp.sans}, thickness={exp.thickness}, dae='{rtype}', aperture='{self.aperture_string(settings.a2_setting_sans)}')\n" + script += self.get_indent_string(indent) + f"do_sans(title="{fulltitle}", pos="{exp.position}", {waitfor}={exp.sans}, thickness={exp.thickness}, dae="{rtype}", aperture="{self.aperture_string(settings.a2_setting_sans)}")\n" script += self.get_indent_string(indent) + exp.post_command + "\n" return script @@ -1903,15 +1903,15 @@ class OpenGenieScriptGenerator: script += f"LOCAL transCount = 0 sansCount = 0 numTRANS={settings.num_trans} numSANS={settings.num_sans}\n\n" script += "SETSCRIPTNAME THIS_PROCEDURE()\n\n" - script += f'Sample_par width={settings.sample_width} height={settings.sample_height} geometry="{settings.sample_geometry}"\n\n' + script += f"Sample_par width={settings.sample_width} height={settings.sample_height} geometry="{settings.sample_geometry}"\n\n" if self.is_sans2d: position_list = ",".join([exp.position for exp in runs if exp.position]) if position_list: - script += f'IF NOT CHECK_MOVE_POS("{position_list}")\n' - script += ' PRINTEN "Invalid positions - script aborted"\n' - script += ' RETURN\n' - script += 'ENDIF\n\n' + script += f"IF NOT CHECK_MOVE_POS("{position_list}")\n" + script += " PRINTEN "Invalid positions - script aborted"\n" + script += " RETURN\n" + script += "ENDIF\n\n" script += "IF test\n" script += " RETURN\n" script += "ENDIF\n\n" @@ -1965,7 +1965,7 @@ class OpenGenieScriptGenerator: script += "EXITIF ((transCount >= numTRANS) AND (sansCount >= numSANS))\n\n" script += "ENDLOOP\n\n" - script += 'PRINTN "SCRIPT COMPLETED "\n\n' + script += "PRINTN "SCRIPT COMPLETED "\n\n" return script def do_single_sans(self, exp, settings, do_pre_com, do_post_com): @@ -2107,7 +2107,7 @@ class OpenGenieScriptGenerator: return script def create_move_command(self, is_trans, exp, settings): - script = f'MOVE pos="{exp.position}"' + script = f"MOVE pos="{exp.position}"" fulltitle = f"{{{exp.sample}}}" if exp.background: fulltitle += f"_{{{exp.background}}}" @@ -2120,14 +2120,14 @@ class OpenGenieScriptGenerator: if is_trans: script += self.write_wait_for(exp.trans_wait, exp.trans) if fulltitle: - script += f' title="{fulltitle}_TRANS"' + script += f" title="{fulltitle}_TRANS"" else: script += self.write_wait_for(exp.sans_wait, exp.sans) if fulltitle: if settings.collection_mode in ["TRANSSANS_Events", "TRANSSANS_Histogram"]: - script += f' title="{fulltitle}_TRANS_SANS"' + script += f" title="{fulltitle}_TRANS_SANS"" else: - script += f' title="{fulltitle}_SANS"' + script += f" title="{fulltitle}_SANS"" return script + "\n" def write_wait_for(self, waitfor, length): @@ -2261,7 +2261,7 @@ setup( "License :: OSI Approved :: MIT License", "Operating System :: OS Independent", ], - python_requires='>=3.6', + python_requires=">=3.6", ) # Additional metadata for localization and resource management can be added as needed. @@ -2292,8 +2292,8 @@ class Resources: @property def resource_manager(self): if self.resource_man is None: - locale_dir = os.path.join(os.path.dirname(__file__), 'locale') - self.resource_man = gettext.translation('resources', localedir=locale_dir, languages=[self.resource_culture]) + locale_dir = os.path.join(os.path.dirname(__file__), "locale") + self.resource_man = gettext.translation("resources", localedir=locale_dir, languages=[self.resource_culture]) return self.resource_man @property @@ -2307,7 +2307,7 @@ class Resources: # Example usage resources = Resources() -resources.culture = 'en_US' # Set the desired culture +resources.culture = "en_US" # Set the desired culture _ = resources.resource_manager.gettext # Example of using the localized string @@ -2363,23 +2363,23 @@ To read this `.ini` file in Python, you can use the following code: import configparser config = configparser.ConfigParser() -config.read('resources.ini') +config.read("resources.ini") # Accessing resheader values -resmimetype = config['resheader']['resmimetype'] -version = config['resheader']['version'] -reader = config['resheader']['reader'] -writer = config['resheader']['writer'] +resmimetype = config["resheader"]["resmimetype"] +version = config["resheader"]["version"] +reader = config["resheader"]["reader"] +writer = config["resheader"]["writer"] # Accessing data values -name1 = config['data']['Name1'] -color1 = config['data']['Color1'] -bitmap1 = config['data']['Bitmap1'] -icon1 = config['data']['Icon1'] +name1 = config["data"]["Name1"] +color1 = config["data"]["Color1"] +bitmap1 = config["data"]["Bitmap1"] +icon1 = config["data"]["Icon1"] # Accessing metadata comments -name1_comment = config['metadata']['Name1.comment'] -icon1_comment = config['metadata']['Icon1.comment'] +name1_comment = config["metadata"]["Name1.comment"] +icon1_comment = config["metadata"]["Icon1.comment"] print(f"Name1: {name1} (Comment: {name1_comment})") print(f"Color1: {color1}") @@ -2418,118 +2418,118 @@ To read and write these settings in Python, you can use the following code: import configparser class Settings: - def __init__(self, filename='settings.ini'): + def __init__(self, filename="settings.ini"): self.config = configparser.ConfigParser() self.filename = filename self.config.read(self.filename) @property def last_instrument(self): - return self.config['DEFAULT']['LastInstrument'] + return self.config["DEFAULT"]["LastInstrument"] @last_instrument.setter def last_instrument(self, value): - self.config['DEFAULT']['LastInstrument'] = value + self.config["DEFAULT"]["LastInstrument"] = value self.save() @property def sample_height(self): - return self.config['DEFAULT']['SampleHeight'] + return self.config["DEFAULT"]["SampleHeight"] @sample_height.setter def sample_height(self, value): - self.config['DEFAULT']['SampleHeight'] = value + self.config["DEFAULT"]["SampleHeight"] = value self.save() @property def sample_width(self): - return self.config['DEFAULT']['SampleWidth'] + return self.config["DEFAULT"]["SampleWidth"] @sample_width.setter def sample_width(self, value): - self.config['DEFAULT']['SampleWidth'] = value + self.config["DEFAULT"]["SampleWidth"] = value self.save() @property def geometry(self): - return self.config['DEFAULT']['Geometry'] + return self.config["DEFAULT"]["Geometry"] @geometry.setter def geometry(self, value): - self.config['DEFAULT']['Geometry'] = value + self.config["DEFAULT"]["Geometry"] = value self.save() @property def run_order(self): - return self.config['DEFAULT']['RunOrder'] + return self.config["DEFAULT"]["RunOrder"] @run_order.setter def run_order(self, value): - self.config['DEFAULT']['RunOrder'] = value + self.config["DEFAULT"]["RunOrder"] = value self.save() @property def num_trans(self): - return self.config['DEFAULT']['NumTrans'] + return self.config["DEFAULT"]["NumTrans"] @num_trans.setter def num_trans(self, value): - self.config['DEFAULT']['NumTrans'] = value + self.config["DEFAULT"]["NumTrans"] = value self.save() @property def num_sans(self): - return self.config['DEFAULT']['NumSans'] + return self.config["DEFAULT"]["NumSans"] @num_sans.setter def num_sans(self, value): - self.config['DEFAULT']['NumSans'] = value + self.config["DEFAULT"]["NumSans"] = value self.save() @property def loop_per_run(self): - return self.config['DEFAULT']['LoopPerRun'] + return self.config["DEFAULT"]["LoopPerRun"] @loop_per_run.setter def loop_per_run(self, value): - self.config['DEFAULT']['LoopPerRun'] = value + self.config["DEFAULT"]["LoopPerRun"] = value self.save() @property def a2_setting_sans(self): - return self.config['DEFAULT']['A2SettingSans'] + return self.config["DEFAULT"]["A2SettingSans"] @a2_setting_sans.setter def a2_setting_sans(self, value): - self.config['DEFAULT']['A2SettingSans'] = value + self.config["DEFAULT"]["A2SettingSans"] = value self.save() @property def a2_setting_trans(self): - return self.config['DEFAULT']['A2SettingTrans'] + return self.config["DEFAULT"]["A2SettingTrans"] @a2_setting_trans.setter def a2_setting_trans(self, value): - self.config['DEFAULT']['A2SettingTrans'] = value + self.config["DEFAULT"]["A2SettingTrans"] = value self.save() @property def collection_mode(self): - return self.config['DEFAULT']['CollectionMode'] + return self.config["DEFAULT"]["CollectionMode"] @collection_mode.setter def collection_mode(self, value): - self.config['DEFAULT']['CollectionMode'] = value + self.config["DEFAULT"]["CollectionMode"] = value self.save() def save(self): - with open(self.filename, 'w') as configfile: + with open(self.filename, "w") as configfile: self.config.write(configfile) # Example usage settings = Settings() print(settings.last_instrument) -settings.last_instrument = 'LOQ' +settings.last_instrument = "LOQ" print(settings.last_instrument) ``` @@ -2572,118 +2572,118 @@ To read and write these settings in Python, you can use the following code: import configparser class Settings: - def __init__(self, filename='settings.ini'): + def __init__(self, filename="settings.ini"): self.config = configparser.ConfigParser() self.filename = filename self.config.read(self.filename) @property def last_instrument(self): - return self.config['DEFAULT']['LastInstrument'] + return self.config["DEFAULT"]["LastInstrument"] @last_instrument.setter def last_instrument(self, value): - self.config['DEFAULT']['LastInstrument'] = value + self.config["DEFAULT"]["LastInstrument"] = value self.save() @property def sample_height(self): - return self.config['DEFAULT']['SampleHeight'] + return self.config["DEFAULT"]["SampleHeight"] @sample_height.setter def sample_height(self, value): - self.config['DEFAULT']['SampleHeight'] = value + self.config["DEFAULT"]["SampleHeight"] = value self.save() @property def sample_width(self): - return self.config['DEFAULT']['SampleWidth'] + return self.config["DEFAULT"]["SampleWidth"] @sample_width.setter def sample_width(self, value): - self.config['DEFAULT']['SampleWidth'] = value + self.config["DEFAULT"]["SampleWidth"] = value self.save() @property def geometry(self): - return self.config['DEFAULT']['Geometry'] + return self.config["DEFAULT"]["Geometry"] @geometry.setter def geometry(self, value): - self.config['DEFAULT']['Geometry'] = value + self.config["DEFAULT"]["Geometry"] = value self.save() @property def run_order(self): - return self.config['DEFAULT']['RunOrder'] + return self.config["DEFAULT"]["RunOrder"] @run_order.setter def run_order(self, value): - self.config['DEFAULT']['RunOrder'] = value + self.config["DEFAULT"]["RunOrder"] = value self.save() @property def num_trans(self): - return self.config['DEFAULT']['NumTrans'] + return self.config["DEFAULT"]["NumTrans"] @num_trans.setter def num_trans(self, value): - self.config['DEFAULT']['NumTrans'] = value + self.config["DEFAULT"]["NumTrans"] = value self.save() @property def num_sans(self): - return self.config['DEFAULT']['NumSans'] + return self.config["DEFAULT"]["NumSans"] @num_sans.setter def num_sans(self, value): - self.config['DEFAULT']['NumSans'] = value + self.config["DEFAULT"]["NumSans"] = value self.save() @property def loop_per_run(self): - return self.config['DEFAULT']['LoopPerRun'] + return self.config["DEFAULT"]["LoopPerRun"] @loop_per_run.setter def loop_per_run(self, value): - self.config['DEFAULT']['LoopPerRun'] = value + self.config["DEFAULT"]["LoopPerRun"] = value self.save() @property def a2_setting_sans(self): - return self.config['DEFAULT']['A2SettingSans'] + return self.config["DEFAULT"]["A2SettingSans"] @a2_setting_sans.setter def a2_setting_sans(self, value): - self.config['DEFAULT']['A2SettingSans'] = value + self.config["DEFAULT"]["A2SettingSans"] = value self.save() @property def a2_setting_trans(self): - return self.config['DEFAULT']['A2SettingTrans'] + return self.config["DEFAULT"]["A2SettingTrans"] @a2_setting_trans.setter def a2_setting_trans(self, value): - self.config['DEFAULT']['A2SettingTrans'] = value + self.config["DEFAULT"]["A2SettingTrans"] = value self.save() @property def collection_mode(self): - return self.config['DEFAULT']['CollectionMode'] + return self.config["DEFAULT"]["CollectionMode"] @collection_mode.setter def collection_mode(self, value): - self.config['DEFAULT']['CollectionMode'] = value + self.config["DEFAULT"]["CollectionMode"] = value self.save() def save(self): - with open(self.filename, 'w') as configfile: + with open(self.filename, "w") as configfile: self.config.write(configfile) # Example usage settings = Settings() print(settings.last_instrument) -settings.last_instrument = 'LOQ' +settings.last_instrument = "LOQ" print(settings.last_instrument) ``` @@ -3134,22 +3134,22 @@ import tkinter as tk from configparser import ConfigParser class ExperimentSettings: - def __init__(self, config_file='settings.ini'): + def __init__(self, config_file="settings.ini"): self.config = ConfigParser() self.config.read(config_file) self.config_file = config_file - self._order = tk.StringVar(value=self.config['DEFAULT'].get('RunOrder', 'TransFirst')) - self._num_sans = tk.IntVar(value=self.config['DEFAULT'].getint('NumSans', 1)) - self._num_trans = tk.IntVar(value=self.config['DEFAULT'].getint('NumTrans', 1)) - self._loop_per_run = tk.BooleanVar(value=self.config['DEFAULT'].getboolean('LoopPerRun', False)) - self._a2_setting_sans = tk.StringVar(value=self.config['DEFAULT'].get('A2SettingSans', 'Large')) - self._a2_setting_trans = tk.StringVar(value=self.config['DEFAULT'].get('A2SettingTrans', 'Large')) - self._sample_geometry = tk.StringVar(value=self.config['DEFAULT'].get('Geometry', 'Flat Plate')) - self._sample_height = tk.DoubleVar(value=self.config['DEFAULT'].getfloat('SampleHeight', 11)) - self._sample_width = tk.DoubleVar(value=self.config['DEFAULT'].getfloat('SampleWidth', 11)) + self._order = tk.StringVar(value=self.config["DEFAULT"].get("RunOrder", "TransFirst")) + self._num_sans = tk.IntVar(value=self.config["DEFAULT"].getint("NumSans", 1)) + self._num_trans = tk.IntVar(value=self.config["DEFAULT"].getint("NumTrans", 1)) + self._loop_per_run = tk.BooleanVar(value=self.config["DEFAULT"].getboolean("LoopPerRun", False)) + self._a2_setting_sans = tk.StringVar(value=self.config["DEFAULT"].get("A2SettingSans", "Large")) + self._a2_setting_trans = tk.StringVar(value=self.config["DEFAULT"].get("A2SettingTrans", "Large")) + self._sample_geometry = tk.StringVar(value=self.config["DEFAULT"].get("Geometry", "Flat Plate")) + self._sample_height = tk.DoubleVar(value=self.config["DEFAULT"].getfloat("SampleHeight", 11)) + self._sample_width = tk.DoubleVar(value=self.config["DEFAULT"].getfloat("SampleWidth", 11)) self._sample_width_enabled = tk.BooleanVar(value=True) - self._collection_mode = tk.StringVar(value=self.config['DEFAULT'].get('CollectionMode', 'Events')) + self._collection_mode = tk.StringVar(value=self.config["DEFAULT"].get("CollectionMode", "Events")) self._is_valid = tk.BooleanVar(value=True) self.property_changed = [] @@ -3170,17 +3170,17 @@ class ExperimentSettings: self.save() def save(self): - self.config['DEFAULT']['RunOrder'] = self._order.get() - self.config['DEFAULT']['NumSans'] = str(self._num_sans.get()) - self.config['DEFAULT']['NumTrans'] = str(self._num_trans.get()) - self.config['DEFAULT']['LoopPerRun'] = str(self._loop_per_run.get()) - self.config['DEFAULT']['A2SettingSans'] = self._a2_setting_sans.get() - self.config['DEFAULT']['A2SettingTrans'] = self._a2_setting_trans.get() - self.config['DEFAULT']['Geometry'] = self._sample_geometry.get() - self.config['DEFAULT']['SampleHeight'] = str(self._sample_height.get()) - self.config['DEFAULT']['SampleWidth'] = str(self._sample_width.get()) - self.config['DEFAULT']['CollectionMode'] = self._collection_mode.get() - with open(self.config_file, 'w') as configfile: + self.config["DEFAULT"]["RunOrder"] = self._order.get() + self.config["DEFAULT"]["NumSans"] = str(self._num_sans.get()) + self.config["DEFAULT"]["NumTrans"] = str(self._num_trans.get()) + self.config["DEFAULT"]["LoopPerRun"] = str(self._loop_per_run.get()) + self.config["DEFAULT"]["A2SettingSans"] = self._a2_setting_sans.get() + self.config["DEFAULT"]["A2SettingTrans"] = self._a2_setting_trans.get() + self.config["DEFAULT"]["Geometry"] = self._sample_geometry.get() + self.config["DEFAULT"]["SampleHeight"] = str(self._sample_height.get()) + self.config["DEFAULT"]["SampleWidth"] = str(self._sample_width.get()) + self.config["DEFAULT"]["CollectionMode"] = self._collection_mode.get() + with open(self.config_file, "w") as configfile: self.config.write(configfile) @property @@ -3238,7 +3238,7 @@ class ExperimentSettings: @sample_geometry.setter def sample_geometry(self, value): self._sample_geometry.set(value) - self.sample_width_enabled = value != 'Disc' + self.sample_width_enabled = value != "Disc" @property def sample_height(self): @@ -3588,7 +3588,7 @@ import csv class CsvExporter: @staticmethod def export(file, runs): - with open(file, mode='w', newline='') as csvfile: + with open(file, mode="w", newline="") as csvfile: writer = csv.writer(csvfile) for exp in runs: row = [ @@ -3641,7 +3641,7 @@ if __name__ == "__main__": runs = [ Experiment(position="pos1", trans=10, trans_wait="Minutes", sans=20, sans_wait="Minutes", period="1", sample="Sample1", background="Background1", thickness="1.0", temperature1="25", temperature2="30", field="1.5T", shear_rate1="100", shear_rate2="200", shear_angle1="45", shear_angle2="90", pre_command="Pre Command", post_command="Post Command", rb_number="RB12345") ] - CsvExporter.export('output.csv', runs) + CsvExporter.export("output.csv", runs) ``` In this Python version: @@ -3768,8 +3768,8 @@ if __name__ == "__main__": Experiment(position="pos1", trans=10, trans_wait="Minutes", sans=20, sans_wait="Minutes", period="1", sample="Sample1", background="Background1", thickness="1.0", temperature1="25", temperature2="30", field="1.5T", shear_rate1="100", shear_rate2="200", shear_angle1="45", shear_angle2="90", pre_command="Pre Command", post_command="Post Command", rb_number="RB12345") ] excel_io = ExcelIO() - excel_io.export_data_to_excel(runs, 'output.xlsx') - excel_io.open_excel_workbook('output.xlsx') + excel_io.export_data_to_excel(runs, "output.xlsx") + excel_io.open_excel_workbook("output.xlsx") experiments = excel_io.extract_experiments() for exp in experiments: print(exp.__dict__) From b777b280ebe22f319c6dae7e0484203363906a9b Mon Sep 17 00:00:00 2001 From: Dirk Honecker Date: Sat, 4 Jan 2025 08:28:30 +0000 Subject: [PATCH 21/88] runorder fixed by ordinal number instead of string --- Models/ExperimentSettings.py | 3 ++- Models/RunOrder.py | 25 ++++++++++++++++++++----- SettingsPanel.py | 2 +- 3 files changed, 23 insertions(+), 7 deletions(-) diff --git a/Models/ExperimentSettings.py b/Models/ExperimentSettings.py index 4b1de4e..e0589bc 100644 --- a/Models/ExperimentSettings.py +++ b/Models/ExperimentSettings.py @@ -5,6 +5,7 @@ from Models.InstrumentInfo import InstrumentInfo from Models.Estimation import Estimation from Properties.Settings import Settings +from Models.RunOrder import RunOrder class ExperimentSettings: def __init__(self, settings=None, estimation=None): @@ -12,7 +13,7 @@ def __init__(self, settings=None, estimation=None): self.estimation = estimation if estimation is not None else Estimation() - self._order = tk.StringVar(value=self.settings.run_order) + self._order = tk.StringVar(value=RunOrder.get_choice_string(self.settings.run_order)) self._num_sans = tk.IntVar(value=self.settings.num_sans) self._num_trans = tk.IntVar(value=self.settings.num_trans) self._loop_per_run = tk.BooleanVar(value=self.settings.loop_per_run) diff --git a/Models/RunOrder.py b/Models/RunOrder.py index ca5d07d..400382f 100644 --- a/Models/RunOrder.py +++ b/Models/RunOrder.py @@ -1,10 +1,17 @@ class RunOrder: - ALL_TRANS = [1, "All TRANS first"] - TRANS_FIRST = [2, "Alternate - TRANS first"] - ALL_SANS = [3, "All SANS first"] - SANS_FIRST = [4, "Alternate - SANS first"] + ALL_TRANS = [1, "1: All TRANS first"] + TRANS_FIRST = [2, "2: Alternate - TRANS first"] + ALL_SANS = [3, "3: SANS first"] + SANS_FIRST = [4, "4: Alternate - SANS first"] choices = ( + ALL_TRANS[1], + TRANS_FIRST[1], + ALL_SANS[1], + SANS_FIRST[1] + ) + + list_order = ( ALL_TRANS, TRANS_FIRST, ALL_SANS, @@ -15,6 +22,14 @@ def get_choices(): return RunOrder.choices + @staticmethod + def get_choice_string(ordinal): + for choice in RunOrder.list_order: + if choice[0] == float(ordinal): + return choice[1] + return None + # Example usage if __name__ == "__main__": - print(RunOrder.get_choices()) \ No newline at end of file + print(RunOrder.get_choices()) + print(RunOrder.get_choice_string(1)) \ No newline at end of file diff --git a/SettingsPanel.py b/SettingsPanel.py index 9ef5eb4..2f97c15 100644 --- a/SettingsPanel.py +++ b/SettingsPanel.py @@ -50,7 +50,7 @@ def create_widgets(self): self.com_order = ttk.Combobox(grid_frame, values=self.order_choices) self.com_order["values"]=self.order_choices self.com_order.grid(row=0, column=1, columnspan=2, padx=5, pady=5) - self.com_order.set(self.settings.run_order) # Set default value + self.com_order.set(RunOrder.get_choice_string(self.settings.run_order)) # Set default value ''' # Collection Mode ttk.Label(grid_frame, text="Collection Mode:").grid(row=1, column=0, sticky=tk.E) From 379d2131de5b74cac22bad1da9e6a7c6d69a4940 Mon Sep 17 00:00:00 2001 From: Dirk Honecker Date: Sat, 4 Jan 2025 08:40:43 +0000 Subject: [PATCH 22/88] Geometry order defined by ordinal number instead of string --- Models/ExperimentSettings.py | 3 ++- Models/Geometry.py | 28 ++++++++++++++++++++++------ Properties/Settings.ini | 2 +- SettingsPanel.py | 7 +++---- 4 files changed, 28 insertions(+), 12 deletions(-) diff --git a/Models/ExperimentSettings.py b/Models/ExperimentSettings.py index e0589bc..9e2ca1e 100644 --- a/Models/ExperimentSettings.py +++ b/Models/ExperimentSettings.py @@ -6,6 +6,7 @@ from Models.Estimation import Estimation from Properties.Settings import Settings from Models.RunOrder import RunOrder +from Models.Geometry import Geometry class ExperimentSettings: def __init__(self, settings=None, estimation=None): @@ -19,7 +20,7 @@ def __init__(self, settings=None, estimation=None): self._loop_per_run = tk.BooleanVar(value=self.settings.loop_per_run) self._a2_setting_sans = tk.StringVar(value=self.settings.a2_setting_sans) self._a2_setting_trans = tk.StringVar(value=self.settings.a2_setting_trans) - self._sample_geometry = tk.StringVar(value=self.settings.geometry) + self._sample_geometry = tk.StringVar(value=Geometry.get_choice_string(self.settings.geometry)) self._sample_height = tk.DoubleVar(value=self.settings.sample_height) self._sample_width = tk.DoubleVar(value=self.settings.sample_width) self._sample_width_enabled = tk.BooleanVar(value=True) diff --git a/Models/Geometry.py b/Models/Geometry.py index 6e91165..78ca559 100644 --- a/Models/Geometry.py +++ b/Models/Geometry.py @@ -1,20 +1,36 @@ class Geometry: - DISC = "Disc" - CYLINDRICAL = "Cylindrical" - FLAT_PLATE = "Flat Plate" - SINGLE_CRYSTAL = "Single Crystal" + DISC = [1, "1: Disc"] + CYLINDRICAL = [2, "2: Cylindrical"] + FLAT_PLATE = [3, "3: Flat Plate"] + SINGLE_CRYSTAL = [4, "4: Single Crystal"] + choices = [ + DISC[1], + CYLINDRICAL[1], + FLAT_PLATE[1], + SINGLE_CRYSTAL[1] + ] + + list_order = ( DISC, CYLINDRICAL, FLAT_PLATE, SINGLE_CRYSTAL - ] + ) @staticmethod def get_choices(): return Geometry.choices + @staticmethod + def get_choice_string(ordinal): + for choice in Geometry.list_order: + if choice[0] == float(ordinal): + return choice[1] + return None + # Example usage if __name__ == "__main__": - print(Geometry.get_choices()) \ No newline at end of file + print(Geometry.get_choices()) + print(Geometry.get_choice_string(1)) \ No newline at end of file diff --git a/Properties/Settings.ini b/Properties/Settings.ini index ba14c99..3ddacf2 100644 --- a/Properties/Settings.ini +++ b/Properties/Settings.ini @@ -2,7 +2,7 @@ lastinstrument = LARMOR sampleheight = 10.0 samplewidth = 10.0 -geometry = Flat Plate +geometry = 3 runorder = 1 numtrans = 1 numsans = 1 diff --git a/SettingsPanel.py b/SettingsPanel.py index 2f97c15..20974c4 100644 --- a/SettingsPanel.py +++ b/SettingsPanel.py @@ -44,9 +44,7 @@ def create_widgets(self): # Order ttk.Label(grid_frame, text="Order:").grid(row=0, column=0, sticky=tk.E) - print(self.order_choices) - print(self.settings.run_order) - + self.com_order = ttk.Combobox(grid_frame, values=self.order_choices) self.com_order["values"]=self.order_choices self.com_order.grid(row=0, column=1, columnspan=2, padx=5, pady=5) @@ -109,7 +107,8 @@ def create_widgets(self): ttk.Label(grid_frame, text="Sample Geometry:").grid(row=7, column=0, sticky=tk.E) self.com_sample_geo = ttk.Combobox(grid_frame, values=self.geometry_choices) self.com_sample_geo.grid(row=7, column=1, columnspan=2, padx=5, pady=5) - self.com_sample_geo.set(self.settings.geometry) # Set default value + self.com_sample_geo.set(Geometry.get_choice_string(self.settings.geometry)) # Set default value + # Sample Height ttk.Label(grid_frame, text="Sample Height:").grid(row=8, column=0, sticky=tk.E) From 5e27d97bf81742cf1ddf015abc655177679de2b2 Mon Sep 17 00:00:00 2001 From: Dirk Honecker Date: Sun, 5 Jan 2025 11:18:37 +0000 Subject: [PATCH 23/88] Update and saving parameters, GUI update and resetting when changing instrument --- EstimationPanel.py | 24 ++++++++--- MainWindow.py | 39 +++++++++-------- Models/A2Setting.py | 6 ++- Models/Estimation.py | 35 +++++++++++---- Models/ExperimentSettings.py | 54 +++++++++++++++++------ Models/Geometry.py | 7 +++ Models/RunOrder.py | 10 ++++- Properties/Settings.ini | 22 +++++----- Scripting/ScriptGenerator.py | 8 ++-- SettingsPanel.py | 65 ++++++++++++++++++++-------- ViewModels/DataGridVM.py | 47 -------------------- ViewModels/EstimationPanelVM.py | 28 ------------ ViewModels/SettingsPanelVM.py | 76 --------------------------------- ViewModels/WaitForUnits.py | 4 -- 14 files changed, 193 insertions(+), 232 deletions(-) delete mode 100644 ViewModels/DataGridVM.py delete mode 100644 ViewModels/EstimationPanelVM.py delete mode 100644 ViewModels/SettingsPanelVM.py delete mode 100644 ViewModels/WaitForUnits.py diff --git a/EstimationPanel.py b/EstimationPanel.py index 545a462..8898781 100644 --- a/EstimationPanel.py +++ b/EstimationPanel.py @@ -1,6 +1,5 @@ import tkinter as tk from tkinter import ttk -from ViewModels.EstimationPanelVM import EstimationPanelVM from Models.Estimation import Estimation from Properties.Settings import Settings @@ -8,8 +7,8 @@ class EstimationPanel(ttk.Frame): def __init__(self, parent, estimation = None, settings = None): super().__init__(parent) self.on_calculate_clicked = None - self.estimation = estimation if estimation is not None else Estimation() self.settings = settings if settings is not None else Settings() + self.estimation = estimation if estimation is not None else Estimation(settings=self.settings) self.create_widgets() @@ -29,19 +28,23 @@ def create_widgets(self): ttk.Label(grid_frame, text="Est. count rate:").grid(row=0, column=0, sticky=tk.E) count_rate_frame = ttk.Frame(grid_frame) count_rate_frame.grid(row=0, column=1, columnspan=2) - self.txt_count_rate = ttk.Entry(count_rate_frame, width=10, textvariable=self.estimation.count_rate) + self.txt_count_rate = ttk.Entry(count_rate_frame, width=10) self.txt_count_rate.insert(0, self.settings.count_rate) # Default value self.txt_count_rate.grid(row=0, column=0, padx=5, pady=5) ttk.Label(count_rate_frame, text="µA/hr").grid(row=0, column=1) + self.txt_count_rate.bind("", self.update_count_rate) # Est. time between moves ttk.Label(grid_frame, text="Est. time between moves:").grid(row=1, column=0, sticky=tk.E) move_time_frame = ttk.Frame(grid_frame) move_time_frame.grid(row=1, column=1, columnspan=2) - self.txt_move_time = ttk.Entry(move_time_frame, width=10, textvariable=self.estimation.move_time) + self.txt_move_time = ttk.Entry(move_time_frame, width=10) self.txt_move_time.insert(0, self.settings.move_time) # Default value self.txt_move_time.grid(row=0, column=0, padx=5, pady=5) ttk.Label(move_time_frame, text="seconds").grid(row=0, column=1) + self.txt_move_time.bind("", self.update_move_time) + + # Est. script time ttk.Label(grid_frame, text="Est. script time:").grid(row=2, column=0, sticky=tk.E) @@ -54,6 +57,17 @@ def create_widgets(self): self.btn_calculate = ttk.Button(grid_frame, text="Calculate", command=self.btn_calculate_click) self.btn_calculate.grid(row=3, column=1, columnspan=2, pady=10) + + # Define the Settings update methods + def update_count_rate(self, event): + self.settings.count_rate = self.txt_count_rate.get() + + def update_move_time(self, event): + self.settings.move_time = self.txt_move_time.get() + + + + def btn_calculate_click(self): if self.on_calculate_clicked: self.on_calculate_clicked() @@ -62,7 +76,7 @@ def btn_calculate_click(self): if __name__ == "__main__": root = tk.Tk() - estimation = EstimationPanelVM() + estimation = Estimation() panel = EstimationPanel(root, estimation) panel.grid(sticky="nsew") root.grid_rowconfigure(0, weight=1) diff --git a/MainWindow.py b/MainWindow.py index b20dac8..6754adf 100644 --- a/MainWindow.py +++ b/MainWindow.py @@ -2,9 +2,6 @@ from tkinter import ttk, filedialog, messagebox from tkinter.scrolledtext import ScrolledText from Models.DataCollection import DataCollection -from ViewModels.DataGridVM import DataGridVM -#from ViewModels.SettingsPanelVM import SettingsPanelVM -#from ViewModels.InstrumentVM import InstrumentVM from Models.InstrumentInfo import InstrumentInfo from Models.Estimation import Estimation from IO.ExcelIO import ExcelIO @@ -28,18 +25,12 @@ def __init__(self,parent): self.parent.title("SANS Script GUI") self.parent.geometry("800x600") - #self.data = DataGridVM() self.settings = Settings() - self.estimation = Estimation() + self.estimation = Estimation(settings=self.settings) self.settings.experiment = ExperimentSettings(settings=self.settings, estimation=self.estimation) self.inst_info = InstrumentInfo(settings=self.settings) - #self.settings.panel = SettingsPanel(self, inst_info=self.inst_info,settings=self.settings) - - - #self.instrument = InstrumentVM(self.settings.experiment, self.estimate) - self.create_widgets() self.load_last_instrument() @@ -86,7 +77,7 @@ def create_widgets(self): file_menu = tk.Menu(self.mnu_main, tearoff=0) self.mnu_main.add_cascade(label="File", menu=file_menu) - file_menu.add_command(label="Export to Excel", command=self.mnu_export_to_excel_click) + #file_menu.add_command(label="Export to Excel", command=self.mnu_export_to_excel_click) file_menu.add_command(label="Save as CSV", command=self.mnu_save_csv_click) file_menu.add_separator() file_menu.add_command(label="Exit", command=self.mnu_exit_click) @@ -94,10 +85,10 @@ def create_widgets(self): instrument_menu = tk.Menu(self.mnu_main, tearoff=0) self.mnu_main.add_cascade(label="Instrument", menu=instrument_menu) - instrument_menu.add_radiobutton(label="LARMOR", variable=self.settings.experiment.instrument, value="LARMOR", command=self.update_settings_panel) - instrument_menu.add_radiobutton(label="LOQ", variable=self.settings.experiment.instrument, value="LOQ", command=self.update_settings_panel) - instrument_menu.add_radiobutton(label="SANS2D", variable=self.settings.experiment.instrument, value="SANS2D", command=self.update_settings_panel) - instrument_menu.add_radiobutton(label="ZOOM", variable=self.settings.experiment.instrument, value="ZOOM", command=self.update_settings_panel) + instrument_menu.add_radiobutton(label="LARMOR", variable=self.settings.experiment.instrument, value="LARMOR", command=self.update_panel) + instrument_menu.add_radiobutton(label="LOQ", variable=self.settings.experiment.instrument, value="LOQ", command=self.update_panel) + instrument_menu.add_radiobutton(label="SANS2D", variable=self.settings.experiment.instrument, value="SANS2D", command=self.update_panel) + instrument_menu.add_radiobutton(label="ZOOM", variable=self.settings.experiment.instrument, value="ZOOM", command=self.update_panel) """ def onFrameConfigure(self, event): self.canvas.configure(scrollregion=self.canvas.bbox("all")) @@ -110,7 +101,21 @@ def update_settings_panel(self): self.pnl_settings.grid(row=0, column=0, sticky="ew") self.pnl_settings.on_preview_clicked = self.pnl_settings_on_preview_clicked self.pnl_settings.on_write_clicked = self.pnl_settings_on_write_clicked - + + def update_estimation_panel(self): + self.pnl_estimation.destroy() + self.inst_info = InstrumentInfo(settings=self.settings) + self.pnl_estimation = EstimationPanel(self, estimation=self.estimation,settings=self.settings) + self.pnl_estimation.grid(row=1, column=0, sticky="ew") + self.pnl_estimation.on_preview_clicked = self.pnl_settings_on_preview_clicked + self.pnl_estimation.on_write_clicked = self.pnl_settings_on_write_clicked + self.pnl_estimation.on_calculate_clicked = self.pnl_estimate_on_calculate_clicked + + def update_panel(self): + self.update_settings_panel() + self.update_estimation_panel() + + def load_last_instrument(self): inst = self.settings.last_instrument # Replace with actual settings retrieval self.settings.experiment.is_instrument = inst @@ -142,7 +147,7 @@ def pnl_settings_on_preview_clicked(self): def pnl_estimate_on_calculate_clicked(self): constructor = TableDataConstructor(TableEntries) self.data = constructor.create_experiments(self.data_frame.data.values()) - self.estimation.script_time = self.script_generator.estimate_run_time(self.data.runs, self.settings.experiment, self.estimation) + self.estimation.script_time = self.script_generator.estimate_run_time(self.data, self.settings.experiment, self.estimation) def btn_create_new_table_click(self): self.data.clear() diff --git a/Models/A2Setting.py b/Models/A2Setting.py index 0921902..9533cac 100644 --- a/Models/A2Setting.py +++ b/Models/A2Setting.py @@ -45,4 +45,8 @@ def get_ZOOM_choices(): # Example usage if __name__ == "__main__": - print(A2Setting.get_LOQ_choices()) \ No newline at end of file + print(A2Setting.get_LOQ_choices()[0]) + + + + diff --git a/Models/Estimation.py b/Models/Estimation.py index 53b1689..2f99e71 100644 --- a/Models/Estimation.py +++ b/Models/Estimation.py @@ -1,14 +1,14 @@ import tkinter as tk - +from Properties.Settings import Settings class Estimation: - def __init__(self, count_rate=40, move_time=120, script_time = 0): - - self._count_rate = tk.DoubleVar(value=count_rate) - self._move_time = tk.DoubleVar(value=move_time) - self._script_time = tk.DoubleVar(value=script_time) + def __init__(self, settings=None): + self.settings = settings if settings is not None else Settings() + self._count_rate = tk.DoubleVar(value=self.settings.count_rate) + self._move_time = tk.DoubleVar(value=self.settings.move_time) + self._script_time = tk.DoubleVar(value=0) self._is_valid = tk.BooleanVar(value=True) @@ -18,6 +18,15 @@ def __init__(self, count_rate=40, move_time=120, script_time = 0): self._count_rate.trace_add("write", self.validate) self._move_time.trace_add("write", self.validate) self._script_time.trace_add("write", self.validate) + + self._count_rate.trace_add("write", self.on_property_changed) + self._move_time.trace_add("write", self.on_property_changed) + + self._count_rate.trace_add("write", self.save_changes) + self._move_time.trace_add("write", self.save_changes) + + + @property def count_rate(self): @@ -55,9 +64,13 @@ def is_valid(self, value): self._is_valid.set(value) self.on_property_changed("is_valid") - def on_property_changed(self, name): + def on_property_changed(self, *args): for callback in self.property_changed: - callback(name) + callback(args[0]) + + def save_changes(self, *args): + self.settings.count_rate = self.count_rate + self.settings.move_time = self.move_time def validate(self, *args): error_message = "" @@ -74,6 +87,12 @@ def validate(self, *args): return error_message + def update_count_rate(self): + self._count_rate.set(self.settings.count_rate) + + def update_move_time(self): + self._move_time.set(self.settings.move_time) + # Example usage if __name__ == "__main__": def on_property_changed(name): diff --git a/Models/ExperimentSettings.py b/Models/ExperimentSettings.py index 9e2ca1e..1b08735 100644 --- a/Models/ExperimentSettings.py +++ b/Models/ExperimentSettings.py @@ -11,7 +11,7 @@ class ExperimentSettings: def __init__(self, settings=None, estimation=None): self.settings = settings if settings is not None else Settings() - self.estimation = estimation if estimation is not None else Estimation() + self.estimation = estimation if estimation is not None else Estimation(settings=self.settings) self._order = tk.StringVar(value=RunOrder.get_choice_string(self.settings.run_order)) @@ -44,6 +44,24 @@ def __init__(self, settings=None, estimation=None): self._sample_width.trace_add("write", self.on_property_changed) self._collection_mode.trace_add("write", self.on_property_changed) + self._num_sans.trace_add("write", self.validate) + self._num_trans.trace_add("write", self.validate) + self._sample_height.trace_add("write", self.validate) + self._sample_width.trace_add("write", self.validate) + self._loop_per_run.trace_add("write", self.validate) + + + self._order.trace_add("write", self.save_changes) + self._num_sans.trace_add("write", self.save_changes) + self._num_trans.trace_add("write", self.save_changes) + self._loop_per_run.trace_add("write", self.save_changes) + self._a2_setting_sans.trace_add("write", self.save_changes) + self._a2_setting_trans.trace_add("write", self.save_changes) + self._sample_geometry.trace_add("write", self.save_changes) + self._sample_height.trace_add("write", self.save_changes) + self._sample_width.trace_add("write", self.save_changes) + self._collection_mode.trace_add("write", self.save_changes) + self.larmor_aperture_sizes=A2Setting.get_LARMOR_choices() self.loq_aperture_sizes=A2Setting.get_LOQ_choices() self.sans2d_aperture_sizes=A2Setting.get_SANS2D_choices() @@ -87,7 +105,8 @@ def __call__(self, order=None, num_sans=None, num_trans=None, loop_per_run=None, def on_property_changed(self, *args): for callback in self.property_changed: callback(args[0]) - self.settings.save() + + def update_instrument(self, *args): if self.is_instrument == "LARMOR": @@ -98,23 +117,31 @@ def update_instrument(self, *args): self.set_instrument(self.is_instrument, "A6 Setting:", self.sans2d_aperture_sizes,"Data Acquisition:",self.sans2d_data_collection, 40) elif self.is_instrument == "ZOOM": self.set_instrument(self.is_instrument, "A6 Setting:", self.zoom_aperture_sizes,"Data Acquisition:",self.zoom_data_collection, 40) + + + def set_instrument(self, instrument, a2_label, a2_choices, data_collection_label, data_collection_choices, count_rate): + + self.settings.last_instrument = instrument + self.estimation.count_rate = count_rate + self.count_rate = count_rate + self.settings.count_rate = count_rate + - def set_instrument(self, instrument, a2_label, a2_choices, data_collection_label, data_collection_choices, count_rate): - - - self.settings.is_instrument = instrument - self.a2_label = a2_label self.a2_choices = a2_choices self.data_collection_label = data_collection_label self.data_collection_choices = data_collection_choices self.a2_trans_enabled = True self.collection_mode_enabled = True - self.estimation.count_rate = count_rate + + # set to default 1st entry in choices + self.settings.a2_setting_sans = a2_choices[0] + self.settings.a2_setting_trans = a2_choices[0] + self.settings.collection_mode = 'Events' - ''' - def save(self): + + def save_changes(self, *args): self.settings.run_order = self.order self.settings.num_sans = self.num_sans self.settings.num_trans = self.num_trans @@ -125,8 +152,7 @@ def save(self): self.settings.sample_height = self.sample_height self.settings.sample_width = self.sample_width self.settings.collection_mode = self.collection_mode - self.settings.save() - ''' + @property def order(self): @@ -237,8 +263,10 @@ def is_valid(self): def is_valid(self, value): self._is_valid.set(value) - def validate(self): + def validate(self, *args): error_message = "" + if self.loop_per_run < 0: + error_message = "Number of Loops per runs cannot be negative!" if self.num_sans < 0: error_message = "Number of SANS runs cannot be negative!" elif self.num_trans < 0: diff --git a/Models/Geometry.py b/Models/Geometry.py index 78ca559..b8c2269 100644 --- a/Models/Geometry.py +++ b/Models/Geometry.py @@ -30,6 +30,13 @@ def get_choice_string(ordinal): return choice[1] return None + @staticmethod + def get_ordinal_from_string(choice_string): + for choice in Geometry.list_order: + if choice[1] == choice_string: + return choice[0] + return None + # Example usage if __name__ == "__main__": print(Geometry.get_choices()) diff --git a/Models/RunOrder.py b/Models/RunOrder.py index 400382f..a05cd41 100644 --- a/Models/RunOrder.py +++ b/Models/RunOrder.py @@ -29,7 +29,15 @@ def get_choice_string(ordinal): return choice[1] return None + @staticmethod + def get_ordinal_from_string(choice_string): + for choice in RunOrder.list_order: + if choice[1] == choice_string: + return choice[0] + return None + # Example usage if __name__ == "__main__": print(RunOrder.get_choices()) - print(RunOrder.get_choice_string(1)) \ No newline at end of file + print(RunOrder.get_choice_string(1)) + print(RunOrder.get_ordinal_from_string("1: All TRANS first")) \ No newline at end of file diff --git a/Properties/Settings.ini b/Properties/Settings.ini index 3ddacf2..8c9537d 100644 --- a/Properties/Settings.ini +++ b/Properties/Settings.ini @@ -1,16 +1,16 @@ [DEFAULT] -lastinstrument = LARMOR -sampleheight = 10.0 -samplewidth = 10.0 -geometry = 3 -runorder = 1 -numtrans = 1 +lastinstrument = LOQ +sampleheight = 12 +samplewidth = 12 +geometry = 4 +runorder = 3 +numtrans = 5 numsans = 1 -loopperrun = False -a2settingsans = Large -a2settingtrans = Large +loopperrun = True +a2settingsans = Large = 17 mm +a2settingtrans = Large = 17 mm collectionmode = Events tableentries = "POSITION, TRANS, TRANS_WAIT, SANS, SANS_WAIT, PERIOD, SAMPLE_IDENTIFICATION_STRING, BACKGROUND, THICKNESS, TEMPERATURE, TEMPERATURE2, FIELD, SHEAR_RATE_1, SHEAR_RATE_2, SHEAR_ANGLE_1, SHEAR_ANGLE_2, PRE_COMMAND, POST_COMMAND, RB_NUMBER" -countrate = 38 -movetime = 120 +countrate = 170 +movetime = 60.0 diff --git a/Scripting/ScriptGenerator.py b/Scripting/ScriptGenerator.py index c4891c2..4e2649f 100644 --- a/Scripting/ScriptGenerator.py +++ b/Scripting/ScriptGenerator.py @@ -65,7 +65,7 @@ def generate_runs(self, runs, settings): if not runs: return script - if float(settings.order) == RunOrder.ALL_TRANS[0]: + if float(settings.settings.run_order) == RunOrder.ALL_TRANS[0]: script += self.get_indent_string(1) + f"for i in range({settings.num_trans}):\n" for exp in runs: if exp.trans: @@ -74,7 +74,7 @@ def generate_runs(self, runs, settings): for exp in runs: if exp.sans: script = self.do_sans_run(script, exp, 2, settings) - elif float(settings.order) == RunOrder.ALL_SANS[0]: + elif float(settings.settings.run_order) == RunOrder.ALL_SANS[0]: script += self.get_indent_string(1) + f"for i in range({settings.num_sans}):\n" for exp in runs: if exp.sans: @@ -91,7 +91,7 @@ def generate_runs(self, runs, settings): script += self.get_indent_string(1) + "while True:\n" indent = 2 for exp in runs: - if float(settings.order) == RunOrder.TRANS_FIRST[0]: + if float(settings.settings.run_order) == RunOrder.TRANS_FIRST[0]: if exp.trans: script += self.get_indent_string(indent) + "if count < num_trans:\n" script = self.do_trans_run(script, exp, indent + 1, settings) @@ -247,7 +247,7 @@ def get_minutes(wait, length, count_rate): experiment_settings =ExperimentSettings() settings = experiment_settings(sample_width="10", sample_height="10", sample_geometry="Flat", order="AllTrans", num_trans=1, num_sans=1, collection_mode="Events", a2_setting_trans="Large", a2_setting_sans="Large") estimation=Estimation() - estimate = estimation(count_rate=100, move_time=10) + estimate = estimation(settings=settings) instrument = "LARMOR" generator = ScriptGenerator(instrument) script = generator.generate_script(runs, settings, "my_script") diff --git a/SettingsPanel.py b/SettingsPanel.py index 20974c4..9924c15 100644 --- a/SettingsPanel.py +++ b/SettingsPanel.py @@ -1,7 +1,5 @@ import tkinter as tk from tkinter import ttk -#from ViewModels.SettingsPanelVM import SettingsPanelVM -#from ViewModels.InstrumentVM import InstrumentVM from Properties.Settings import Settings from Models.RunOrder import RunOrder from Models.Geometry import Geometry @@ -15,8 +13,6 @@ def __init__(self, parent,inst_info=None, settings=None): self.parent = parent self.settings = settings if settings is not None else Settings() self.inst_info = inst_info if inst_info is not None else InstrumentInfo() - #self.settings_vm = settings_vm if settings_vm is not None else SettingsPanelVM() - #self.instrument_vm = instrument_vm if instrument_vm is not None else InstrumentVM() self.on_preview_clicked = None self.on_write_clicked = None self.property_changed = [] @@ -49,17 +45,8 @@ def create_widgets(self): self.com_order["values"]=self.order_choices self.com_order.grid(row=0, column=1, columnspan=2, padx=5, pady=5) self.com_order.set(RunOrder.get_choice_string(self.settings.run_order)) # Set default value - ''' - # Collection Mode - ttk.Label(grid_frame, text="Collection Mode:").grid(row=1, column=0, sticky=tk.E) - print(self.settings_vm.collection_mode_choices) - print(settings.collection_mode) - - self.collect_mode = ttk.Combobox(grid_frame, values=self.settings_vm.collection_mode_choices) - self.collect_mode["values"]=self.settings_vm.collection_mode_choices - self.collect_mode.grid(row=1, column=1, columnspan=2, padx=5, pady=5) - self.collect_mode.set(settings.collection_mode) # Set default value - ''' + self.com_order.bind("", self.update_order) + # Do SANS ttk.Label(grid_frame, text="Do SANS:").grid(row=1, column=0, sticky=tk.E) @@ -69,7 +56,8 @@ def create_widgets(self): self.txt_times_sans.insert(0, self.settings.num_sans) # Default value self.txt_times_sans.grid(row=0, column=0, padx=5, pady=5) ttk.Label(sans_frame, text="time(s)").grid(row=0, column=1) - + self.txt_times_sans.bind("", self.update_num_sans) + # Do TRANS ttk.Label(grid_frame, text="Do TRANS:").grid(row=2, column=0, sticky=tk.E) trans_frame = ttk.Frame(grid_frame) @@ -78,17 +66,20 @@ def create_widgets(self): self.txt_times_trans.insert(0, self.settings.num_trans) # Default value self.txt_times_trans.grid(row=0, column=0, padx=5, pady=5) ttk.Label(trans_frame, text="time(s)").grid(row=0, column=1) + self.txt_times_trans.bind("", self.update_num_trans) # Loop over each run self.loop_run_var = tk.BooleanVar(value=self.settings.loop_per_run) # Create BooleanVar and set default value self.chk_loop_run = ttk.Checkbutton(grid_frame, text="Loop over each run?", variable=self.loop_run_var) self.chk_loop_run.grid(row=3, column=0, columnspan=3, pady=5) + self.chk_loop_run.bind("", self.update_loop_per_run) # Collection Mode tk.Label(grid_frame, text=self.inst_info.data_collection_label).grid(row=4, column=0, sticky=tk.E) self.com_collection_mode = ttk.Combobox(grid_frame, values=self.inst_info.data_collection_choices) self.com_collection_mode.grid(row=4, column=1, columnspan=2, padx=5, pady=5) self.com_collection_mode.set(self.settings.collection_mode) # Set default value + self.com_collection_mode.bind("", self.update_collection_mode) # A2 SANS @@ -96,19 +87,21 @@ def create_widgets(self): self.com_a2_sans = ttk.Combobox(grid_frame, values=self.inst_info.a2_choices ) self.com_a2_sans.grid(row=5, column=1, columnspan=2, padx=5, pady=5) self.com_a2_sans.set(self.settings.a2_setting_sans) # Set default value + self.com_a2_sans.bind("", self.update_a2_sans) # A2 TRANS tk.Label(grid_frame, text="TRANS "+self.inst_info.a2_label).grid(row=6, column=0, sticky=tk.E) self.com_a2_trans = ttk.Combobox(grid_frame, values=self.inst_info.a2_choices) self.com_a2_trans.grid(row=6, column=1, columnspan=2, padx=5, pady=5) self.com_a2_trans.set(self.settings.a2_setting_trans) # Set default value + self.com_a2_trans.bind("", self.update_a2_trans) # Sample Geometry ttk.Label(grid_frame, text="Sample Geometry:").grid(row=7, column=0, sticky=tk.E) self.com_sample_geo = ttk.Combobox(grid_frame, values=self.geometry_choices) self.com_sample_geo.grid(row=7, column=1, columnspan=2, padx=5, pady=5) self.com_sample_geo.set(Geometry.get_choice_string(self.settings.geometry)) # Set default value - + self.com_sample_geo.bind("", self.update_sample_geometry) # Sample Height ttk.Label(grid_frame, text="Sample Height:").grid(row=8, column=0, sticky=tk.E) @@ -118,6 +111,7 @@ def create_widgets(self): self.txt_sample_height.insert(0, self.settings.sample_height) # Default value self.txt_sample_height.grid(row=0, column=0, padx=5, pady=5) ttk.Label(sample_height_frame, text="mm").grid(row=0, column=1) + self.txt_sample_height.bind("", self.update_sample_height) # Sample Width ttk.Label(grid_frame, text="Sample Width:").grid(row=9, column=0, sticky=tk.E) @@ -127,6 +121,7 @@ def create_widgets(self): self.txt_sample_width.insert(0, self.settings.sample_width) # Default value self.txt_sample_width.grid(row=0, column=0, padx=5, pady=5) ttk.Label(sample_width_frame, text="mm").grid(row=0, column=1) + self.txt_sample_width.bind("", self.update_sample_width) # Buttons button_frame = ttk.Frame(self) @@ -136,6 +131,42 @@ def create_widgets(self): self.btn_write_script = ttk.Button(button_frame, text="Write Script", command=self.btn_write_script_click) self.btn_write_script.grid(row=0, column=1, padx=5) + # Define the Settings update methods + def update_order(self, event): + self.settings.run_order = RunOrder.get_ordinal_from_string(self.com_order.get()) + + def update_num_sans(self, event): + self.settings.num_sans = self.txt_times_sans.get() + + def update_num_trans(self, event): + self.settings.num_trans = self.txt_times_trans.get() + + def update_loop_per_run(self, event): + self.settings.loop_per_run = self.loop_run_var.get() + + def update_collection_mode(self, event): + self.settings.collection_mode = self.com_collection_mode.get() + + def update_collection_mode(self, event): + self.settings.collection_mode = self.com_collection_mode.get() + + def update_a2_sans(self, event): + self.settings.a2_setting_sans = self.com_a2_sans.get() + + def update_a2_trans(self, event): + self.settings.a2_setting_trans = self.com_a2_trans.get() + + def update_sample_geometry(self, event): + self.settings.geometry = Geometry.get_ordinal_from_string(self.com_sample_geo.get()) + + def update_sample_height(self, event): + self.settings.sample_height = self.txt_sample_height.get() + + def update_sample_width(self, event): + self.settings.sample_width = self.txt_sample_width.get() + + + def btn_preview_script_click(self): if self.on_preview_clicked: self.on_preview_clicked() diff --git a/ViewModels/DataGridVM.py b/ViewModels/DataGridVM.py deleted file mode 100644 index 0b9dfc7..0000000 --- a/ViewModels/DataGridVM.py +++ /dev/null @@ -1,47 +0,0 @@ -import tkinter as tk -from tkinter import StringVar -from tkinter import ttk -from Models.WaitForUnits import WaitForUnits -from Models.Experiment import Experiment - - - -class DataGridVM: - def __init__(self): - self._runs = tk.Variable(value=[]) - self.property_changed = [] - - @property - def runs(self): - return self._runs.get() - - @runs.setter - def runs(self, value): - self._runs.set(value) - self.on_property_changed("runs") - - @staticmethod - def wait_for_choices(): - return WaitForUnits.choices() - - def on_property_changed(self, name): - for callback in self.property_changed: - callback(name) - - - - - -if __name__ == "__main__": - root = tk.Tk() - vm = DataGridVM() - - def on_property_changed(name): - print(f"Property {name} changed") - - vm.property_changed.append(on_property_changed) - - vm.runs = [Experiment(), Experiment()] - print(vm.runs) - - root.mainloop() \ No newline at end of file diff --git a/ViewModels/EstimationPanelVM.py b/ViewModels/EstimationPanelVM.py deleted file mode 100644 index d9e2c76..0000000 --- a/ViewModels/EstimationPanelVM.py +++ /dev/null @@ -1,28 +0,0 @@ -import tkinter as tk - - - -class EstimationPanelVM: - def __init__(self): - self.property_changed = [] - self.count_rate = tk.StringVar() - self.move_time = tk.StringVar() - self.script_time = tk.StringVar() - - def on_property_changed(self, name): - for callback in self.property_changed: - callback(name) - -if __name__ == "__main__": - root = tk.Tk() - vm = EstimationPanelVM() - - def on_property_changed(name): - print(f"Property {name} changed") - - vm.property_changed.append(on_property_changed) - - # Example of triggering property change - vm.on_property_changed("example_property") - - root.mainloop() \ No newline at end of file diff --git a/ViewModels/SettingsPanelVM.py b/ViewModels/SettingsPanelVM.py deleted file mode 100644 index 51fd0c9..0000000 --- a/ViewModels/SettingsPanelVM.py +++ /dev/null @@ -1,76 +0,0 @@ -import tkinter as tk -from tkinter import StringVar -from Models.Estimation import Estimation -from Models.Experiment import Experiment -from Models.ExperimentSettings import ExperimentSettings -from Models.InstrumentInfo import InstrumentInfo -from Models.DataCollection import DataCollection -from Models.RunOrder import RunOrder -from Models.Geometry import Geometry - -class SettingsPanelVM: - def __init__(self, inst_info=None, experiment=None): - self.property_changed = [] - self.order_choices = RunOrder.get_choices() - self.geometry_choices = Geometry.get_choices() - #self.collection_mode_choices = DataCollection.get_choices() - - self._experiment = experiment if experiment is not None else ExperimentSettings() - self._inst_info = inst_info if inst_info is not None else InstrumentInfo() - - def on_property_changed(self, name): - for callback in self.property_changed: - callback(name) - - @property - def experiment(self): - return self._experiment - - @experiment.setter - def experiment(self, value): - self._experiment = value - self.on_property_changed("experiment") - - @property - def inst_info(self): - return self._inst_info - - @inst_info.setter - def inst_info(self, value): - self._inst_info = value - self.on_property_changed("inst_info") - - @staticmethod - def order_choices(): - return RunOrder.get_choices() - - @staticmethod - def geometry_choices(): - return Geometry.get_choices() - ''' - @staticmethod - def collection_mode_choices(): - return DataCollection.get_choices() - ''' - - - - - - - - - -if __name__ == "__main__": - root = tk.Tk() - vm = SettingsPanelVM() - - def on_property_changed(name): - print(f"Property {name} changed") - - vm.property_changed.append(on_property_changed) - - vm.experiment = ExperimentSettings() - vm.inst_info = InstrumentInfo() - - root.mainloop() \ No newline at end of file diff --git a/ViewModels/WaitForUnits.py b/ViewModels/WaitForUnits.py deleted file mode 100644 index 69c86ba..0000000 --- a/ViewModels/WaitForUnits.py +++ /dev/null @@ -1,4 +0,0 @@ -class WaitForUnits: - @staticmethod - def choices(): - return ["Choice1", "Choice2", "Choice3"] \ No newline at end of file From ffde96274c1a4661ab6e827fee2103a6b8193ce1 Mon Sep 17 00:00:00 2001 From: Dirk Honecker Date: Mon, 6 Jan 2025 00:06:43 +0000 Subject: [PATCH 24/88] table size changes with window --- App.py | 4 +- Dialogs/PreviewDialog.py | 4 +- EstimationPanel.py | 12 ++-- MainWindow.py | 52 ++++++++++----- TableWidget.py | 137 ++++++++++++++++++++++++++++++++++++--- 5 files changed, 174 insertions(+), 35 deletions(-) diff --git a/App.py b/App.py index c0f82eb..41ee35e 100644 --- a/App.py +++ b/App.py @@ -6,8 +6,8 @@ def __init__(self): self.root = tk.Tk() self.main_window = MainWindow(self.root) self.main_window.grid(sticky="nsew") - self.root.grid_rowconfigure(1, weight=1) - self.root.grid_columnconfigure(1, weight=1) + self.root.grid_rowconfigure(0, weight=1) + self.root.grid_columnconfigure(0, weight=1) self.root.protocol("WM_DELETE_WINDOW", self.on_close) diff --git a/Dialogs/PreviewDialog.py b/Dialogs/PreviewDialog.py index 3b03c45..c598cac 100644 --- a/Dialogs/PreviewDialog.py +++ b/Dialogs/PreviewDialog.py @@ -16,7 +16,7 @@ def __init__(self, parent, script=None): # Create a frame for the text area with a border border_frame = tk.Frame(self, borderwidth=1, relief="solid") - border_frame.grid(row=0, column=0, padx=5, pady=5, sticky="nsew") + border_frame.grid(row=0, column=0, sticky="nsew") # Create a scrollable text area self.txt_script = scrolledtext.ScrolledText(border_frame, wrap=tk.WORD) @@ -26,7 +26,7 @@ def __init__(self, parent, script=None): self.txt_script.config(state=tk.DISABLED) # Create a frame for the buttons button_frame = tk.Frame(self) - button_frame.grid(row=1, column=0, pady=5, sticky="e") + button_frame.grid(row=1, column=0, sticky="e") # Create the Print Script button (initially hidden) self.btn_print = tk.Button(button_frame, text="Print Script", command=self.print_script) diff --git a/EstimationPanel.py b/EstimationPanel.py index 8898781..e49de9b 100644 --- a/EstimationPanel.py +++ b/EstimationPanel.py @@ -18,10 +18,10 @@ def create_widgets(self): self.grid(sticky="nsew") label = ttk.Label(self, text="Estimated Script Time", font=("Helvetica", 16, "bold")) - label.grid(row=0, column=0, columnspan=3, pady=10) + label.grid(row=0, column=0, columnspan=3) grid_frame = ttk.Frame(self) - grid_frame.grid(row=1, column=0, columnspan=3, padx=10, pady=10, sticky="nsew") + grid_frame.grid(row=1, column=0, columnspan=3, sticky="nsew") # Est. count rate @@ -30,7 +30,7 @@ def create_widgets(self): count_rate_frame.grid(row=0, column=1, columnspan=2) self.txt_count_rate = ttk.Entry(count_rate_frame, width=10) self.txt_count_rate.insert(0, self.settings.count_rate) # Default value - self.txt_count_rate.grid(row=0, column=0, padx=5, pady=5) + self.txt_count_rate.grid(row=0, column=0) ttk.Label(count_rate_frame, text="µA/hr").grid(row=0, column=1) self.txt_count_rate.bind("", self.update_count_rate) @@ -40,7 +40,7 @@ def create_widgets(self): move_time_frame.grid(row=1, column=1, columnspan=2) self.txt_move_time = ttk.Entry(move_time_frame, width=10) self.txt_move_time.insert(0, self.settings.move_time) # Default value - self.txt_move_time.grid(row=0, column=0, padx=5, pady=5) + self.txt_move_time.grid(row=0, column=0) ttk.Label(move_time_frame, text="seconds").grid(row=0, column=1) self.txt_move_time.bind("", self.update_move_time) @@ -51,11 +51,11 @@ def create_widgets(self): script_time_frame = ttk.Frame(grid_frame) script_time_frame.grid(row=2, column=1, columnspan=2) self.txt_estimation = ttk.Entry(script_time_frame, width=15, textvariable=self.estimation.script_time, state="readonly") - self.txt_estimation.grid(row=0, column=0, padx=5, pady=5) + self.txt_estimation.grid(row=0, column=0) # Calculate button self.btn_calculate = ttk.Button(grid_frame, text="Calculate", command=self.btn_calculate_click) - self.btn_calculate.grid(row=3, column=1, columnspan=2, pady=10) + self.btn_calculate.grid(row=3, column=1, columnspan=2) # Define the Settings update methods diff --git a/MainWindow.py b/MainWindow.py index 6754adf..60607fa 100644 --- a/MainWindow.py +++ b/MainWindow.py @@ -23,7 +23,8 @@ def __init__(self,parent): super().__init__(parent) self.parent = parent self.parent.title("SANS Script GUI") - self.parent.geometry("800x600") + #self.parent.geometry("1200x600") + self.parent.resizable(True, True) self.settings = Settings() @@ -33,26 +34,41 @@ def __init__(self,parent): self.create_widgets() self.load_last_instrument() - + def create_widgets(self): #self.data_grid = ttk.Treeview(self) - self.grid_columnconfigure(1, weight=1) - self.grid_rowconfigure(1, weight=1) - - + + # Configure grid weights for the parent container + + #self.parent.rowconfigure(0,weight=1) + self.parent.columnconfigure(0,weight=1) + #self.master.rowconfigure(0,weight=1) + self.master.columnconfigure(0,weight=1) + # Configure grid weights for the frame itself + #self.grid_rowconfigure(0, weight=1) + #self.grid_columnconfigure(0, weight=1) + #self.rowconfigure(0, weight=1) + #self.columnconfigure(0, weight=1) + + # Data Grid with horizontal and vertical scrollbars - self.data_frame = TableWidget(self) - #self.data_frame.grid(row=0, column=1, rowspan=4, sticky="nsew", padx=5, pady=5) - self.data_frame.grid(row=0, column=1, sticky="nsew", padx=5, pady=5) - + self.data_frame = TableWidget(self) + self.data_frame.grid(row=0, column=1, rowspan=3, sticky="nsew") + + #self.data_frame.config(width=800) + + + self.columnconfigure(1, weight=1) + """ for col in self.data_grid["columns"]: self.data_grid.heading(col, text=col.replace("_", " ")) """ + # Settings Panel self.pnl_settings = SettingsPanel(self, inst_info=self.inst_info,settings=self.settings) self.pnl_settings.grid(row=0, column=0, sticky="ew") @@ -64,12 +80,12 @@ def create_widgets(self): self.pnl_estimation.on_calculate_clicked = self.pnl_estimate_on_calculate_clicked # Buttons button_frame = ttk.Frame(self) - button_frame.grid(row=2, column=0, padx=5, pady=5, sticky="w") + button_frame.grid(row=2, column=0, sticky="w") self.btn_create_new_table = ttk.Button(button_frame, text="Create New Table", command=self.btn_create_new_table_click) - self.btn_create_new_table.grid(row=0, column=0, padx=5, pady=5) + self.btn_create_new_table.grid(row=0, column=0 ) self.btn_open_excel = ttk.Button(button_frame, text="Open Excel", command=self.btn_open_excel_click) - self.btn_open_excel.grid(row=0, column=1, padx=5, pady=5) + self.btn_open_excel.grid(row=0, column=1) self.mnu_main = tk.Menu(self.parent) @@ -89,6 +105,8 @@ def create_widgets(self): instrument_menu.add_radiobutton(label="LOQ", variable=self.settings.experiment.instrument, value="LOQ", command=self.update_panel) instrument_menu.add_radiobutton(label="SANS2D", variable=self.settings.experiment.instrument, value="SANS2D", command=self.update_panel) instrument_menu.add_radiobutton(label="ZOOM", variable=self.settings.experiment.instrument, value="ZOOM", command=self.update_panel) + + """ def onFrameConfigure(self, event): self.canvas.configure(scrollregion=self.canvas.bbox("all")) @@ -182,12 +200,12 @@ def mnu_save_csv_click(self): CsvExporter.export(filepath, self.data) except Exception as err: messagebox.showerror("SANS Script - Error", "Could not save as CSV") - + def on_close(self): # Save last_instrument to settings self.settings.save() self.parent.destroy() - + @@ -197,7 +215,7 @@ def on_close(self): root = tk.Tk() app = MainWindow(root) app.grid(sticky="nsew") - root.grid_rowconfigure(0, weight=1) - root.grid_columnconfigure(0, weight=1) + #root.grid_rowconfigure(0, weight=1) + #root.grid_columnconfigure(0, weight=1) app.protocol("WM_DELETE_WINDOW", app.on_close) app.mainloop() \ No newline at end of file diff --git a/TableWidget.py b/TableWidget.py index 132309f..9496e8e 100644 --- a/TableWidget.py +++ b/TableWidget.py @@ -112,9 +112,27 @@ def show(self, callback=None): self.parentframe.rowconfigure(1,weight=1) self.parentframe.columnconfigure(1,weight=1) - self.tablecolheader.grid(row=0,column=1,rowspan=1,sticky="news",pady=0,ipady=0) - self.tablerowheader.grid(row=1,column=0,rowspan=1,sticky="news",pady=0,ipady=0) - self.grid(row=1,column=1,rowspan=1,sticky="news",pady=0,ipady=0) + # Configure grid layout + ''' + #self.parentframe.grid_rowconfigure(0, weight=0) + #self.parentframe.grid_rowconfigure(1, weight=1) + #self.parentframe.grid_rowconfigure(2, weight=0) + + #self.parentframe.grid_columnconfigure(0, weight=0) + #self.parentframe.grid_columnconfigure(1, weight=1) + #self.parentframe.grid_columnconfigure(2, weight=0) + #self.parentframe.grid_columnconfigure(3, weight=0) + ''' + + self.tablecolheader.grid(row=0,column=1,rowspan=1,sticky='news',pady=0,ipady=0) + self.tablerowheader.grid(row=1,column=0,rowspan=1,sticky='news',pady=0,ipady=0) + #self.tablecolheader.grid(row=0, column=1, sticky="ew") + #self.tablerowheader.grid(row=1, column=0, sticky="ns") + self.grid(row=1, column=1, sticky="news",pady=0,ipady=0) + #self.Yscrollbar.grid(row=1, column=2, sticky="ns") + #self.Xscrollbar.grid(row=2, column=1, sticky="ew") + + #self.grid(row=1,column=1,rowspan=1,sticky="news",pady=0,ipady=0) self.adjustColumnWidths() self.redrawTable(callback=callback) @@ -122,18 +140,114 @@ def show(self, callback=None): self.tablecolheader.xview("moveto", 0) self.xview("moveto", 0) return - + ''' + def on_resize(self, event): + self.update_table_size() + + def update_table_size(self): + """Adjust the table width based on the available space in the parent frame.""" + available_width= self.parentframe.winfo_width() + if available_width <= 1.0: + available_width = self.parentframe.master.winfo_width() + print(available_width) + required_width = (self.cellwidth) * self.cols + self.tablewidth = max(available_width, required_width) + self.redrawTable() + ''' + ''' def updateModel(self, model): """Call this method to update the table model""" self.model = model self.rows = self.model.getRowCount() self.cols = self.model.getColumnCount() - self.tablewidth = (self.cellwidth)*self.cols + if self.tablewidth is None: + self.tablewidth = (self.cellwidth)*self.cols self.tablecolheader = CustomColumnHeader(self.parentframe, self) self.tablerowheader = CustomRowHeader(self.parentframe, self) self.createTableFrame() return + ''' + ''' + def redrawTable(self, event=None, callback=None): + self.redrawVisible(event, callback) + return + ''' + ''' + def getVisibleRegion(self): + """Get visible region of table to display""" + + x1, y1 = self.canvasx(0), self.canvasy(0) + #w, h = self.winfo_width(), self.winfo_height() + w,h= self.parentframe.winfo_width(), self.parentframe.winfo_height() + if w <= 1.0 or h <= 1.0: + w, h = self.parentframe.master.winfo_width(), self.parentframe.master.winfo_height() + x2, y2 = self.canvasx(w), self.canvasy(h) + return x1, y1, x2, y2 + + def redrawVisible(self, event=None, callback=None): + """Redraw the visible portion of the canvas""" + + model = self.model + self.rows = self.model.getRowCount() + self.cols = self.model.getColumnCount() + if self.tablewidth is None: + self.tablewidth = (self.cellwidth) * self.cols + self.configure(bg=self.bgcolor) + self.setColPositions() + + #are we drawing a filtered subset of the recs? + if self.filtered == True and self.model.filteredrecs != None: + self.rows = len(self.model.filteredrecs) + self.delete('colrect') + + self.rowrange = range(0,self.rows) + self.configure(scrollregion=(0,0, self.tablewidth+self.x_start, + self.rowheight*self.rows+10)) + + x1, y1, x2, y2 = self.getVisibleRegion() + startvisiblerow, endvisiblerow = self.getVisibleRows(y1, y2) + self.visiblerows = range(startvisiblerow, endvisiblerow) + startvisiblecol, endvisiblecol = self.getVisibleCols(x1, x2) + self.visiblecols = range(startvisiblecol, endvisiblecol) + + if self.cols == 0 or self.rows == 0: + self.delete('entry') + self.delete('rowrect') + self.delete('currentrect') + self.delete('gridline','text') + self.tablerowheader.redraw() + return + + self.drawGrid(startvisiblerow, endvisiblerow) + align = self.align + self.delete('fillrect') + for row in self.visiblerows: + if callback != None: + callback() + for col in self.visiblecols: + colname = model.getColumnName(col) + bgcolor = model.getColorAt(row,col, 'bg') + fgcolor = model.getColorAt(row,col, 'fg') + text = model.getValueAt(row,col) + self.drawText(row, col, text, fgcolor, align) + if bgcolor != None: + self.drawRect(row,col, color=bgcolor) + + #self.drawSelectedCol() + self.tablecolheader.redraw() + self.tablerowheader.redraw(align=self.align, showkeys=self.showkeynamesinheader) + #self.setSelectedRow(self.currentrow) + self.drawSelectedRow() + self.drawSelectedRect(self.currentrow, self.currentcol) + #print self.multiplerowlist + + if len(self.multiplerowlist)>1: + self.tablerowheader.drawSelectedRows(self.multiplerowlist) + self.drawMultipleRows(self.multiplerowlist) + self.drawMultipleCells() + return + ''' def deleteCells(self, rows=None, cols=None): """Clear the cell contents""" @@ -351,11 +465,18 @@ def __init__(self, parent=None, data=None): print (self.table.model.columnNames) #table.model.data[1]["a"] = "XX" #table.model.setValueAt("YY",0,2) - self.table.show() + self.grid(row=0, column=0, sticky="nsew") + #self.parent.grid_rowconfigure(0, weight=1) + #self.parent.grid_columnconfigure(0, weight=1) + + # Bind the resize event to redraw the table + + #self.parent.bind("", self.on_resize) - - + #def on_resize(self, event): + # self.table.update_table_size() + def customOption(self): print("Custom option selected") return From 803972fc5a43c85d71ac75476ea84311cd0af51b Mon Sep 17 00:00:00 2001 From: Dirk Honecker Date: Mon, 6 Jan 2025 00:21:31 +0000 Subject: [PATCH 25/88] regular white space around elements --- Dialogs/PreviewDialog.py | 10 +++++----- EstimationPanel.py | 28 ++++++++++++++-------------- MainWindow.py | 14 +++++++------- SettingsPanel.py | 30 +++++++++++++++--------------- 4 files changed, 41 insertions(+), 41 deletions(-) diff --git a/Dialogs/PreviewDialog.py b/Dialogs/PreviewDialog.py index c598cac..f822d55 100644 --- a/Dialogs/PreviewDialog.py +++ b/Dialogs/PreviewDialog.py @@ -16,26 +16,26 @@ def __init__(self, parent, script=None): # Create a frame for the text area with a border border_frame = tk.Frame(self, borderwidth=1, relief="solid") - border_frame.grid(row=0, column=0, sticky="nsew") + border_frame.grid(row=0, column=0, sticky="nsew",padx=5, pady=5) # Create a scrollable text area self.txt_script = scrolledtext.ScrolledText(border_frame, wrap=tk.WORD) - self.txt_script.grid(row=0, column=0, sticky="nsew") + self.txt_script.grid(row=0, column=0, sticky="nsew",padx=5, pady=5) self.txt_script.insert(tk.END, script) self.txt_script.config(state=tk.DISABLED) # Create a frame for the buttons button_frame = tk.Frame(self) - button_frame.grid(row=1, column=0, sticky="e") + button_frame.grid(row=1, column=0, sticky="e",padx=5, pady=5) # Create the Print Script button (initially hidden) self.btn_print = tk.Button(button_frame, text="Print Script", command=self.print_script) - self.btn_print.grid(row=0, column=0, padx=5) + self.btn_print.grid(row=0, column=0, padx=5, pady=5) self.btn_print.pack_forget() # Hide the button initially # Create the Close button btn_close = tk.Button(button_frame, text="Close", command=self.close) - btn_close.grid(row=0, column=1, padx=5) + btn_close.grid(row=0, column=1, padx=5, pady=5) def set_script(self, script): self.txt_script.delete(1.0, tk.END) diff --git a/EstimationPanel.py b/EstimationPanel.py index e49de9b..6b02877 100644 --- a/EstimationPanel.py +++ b/EstimationPanel.py @@ -18,44 +18,44 @@ def create_widgets(self): self.grid(sticky="nsew") label = ttk.Label(self, text="Estimated Script Time", font=("Helvetica", 16, "bold")) - label.grid(row=0, column=0, columnspan=3) + label.grid(row=0, column=0, columnspan=3,padx=5, pady=5) grid_frame = ttk.Frame(self) - grid_frame.grid(row=1, column=0, columnspan=3, sticky="nsew") + grid_frame.grid(row=1, column=0, columnspan=3, sticky="nsew",padx=5, pady=5) # Est. count rate - ttk.Label(grid_frame, text="Est. count rate:").grid(row=0, column=0, sticky=tk.E) + ttk.Label(grid_frame, text="Est. count rate:").grid(row=0, column=0, sticky=tk.E,padx=5, pady=5) count_rate_frame = ttk.Frame(grid_frame) - count_rate_frame.grid(row=0, column=1, columnspan=2) + count_rate_frame.grid(row=0, column=1, columnspan=2,padx=5, pady=5) self.txt_count_rate = ttk.Entry(count_rate_frame, width=10) self.txt_count_rate.insert(0, self.settings.count_rate) # Default value - self.txt_count_rate.grid(row=0, column=0) - ttk.Label(count_rate_frame, text="µA/hr").grid(row=0, column=1) + self.txt_count_rate.grid(row=0, column=0,padx=5, pady=5) + ttk.Label(count_rate_frame, text="µA/hr").grid(row=0, column=1,padx=5, pady=5) self.txt_count_rate.bind("", self.update_count_rate) # Est. time between moves - ttk.Label(grid_frame, text="Est. time between moves:").grid(row=1, column=0, sticky=tk.E) + ttk.Label(grid_frame, text="Est. time between moves:").grid(row=1, column=0, sticky=tk.E,padx=5, pady=5) move_time_frame = ttk.Frame(grid_frame) - move_time_frame.grid(row=1, column=1, columnspan=2) + move_time_frame.grid(row=1, column=1, columnspan=2,padx=5, pady=5) self.txt_move_time = ttk.Entry(move_time_frame, width=10) self.txt_move_time.insert(0, self.settings.move_time) # Default value - self.txt_move_time.grid(row=0, column=0) - ttk.Label(move_time_frame, text="seconds").grid(row=0, column=1) + self.txt_move_time.grid(row=0, column=0,padx=5, pady=5) + ttk.Label(move_time_frame, text="seconds").grid(row=0, column=1,padx=5, pady=5) self.txt_move_time.bind("", self.update_move_time) # Est. script time - ttk.Label(grid_frame, text="Est. script time:").grid(row=2, column=0, sticky=tk.E) + ttk.Label(grid_frame, text="Est. script time:").grid(row=2, column=0, sticky=tk.E,padx=5, pady=5) script_time_frame = ttk.Frame(grid_frame) - script_time_frame.grid(row=2, column=1, columnspan=2) + script_time_frame.grid(row=2, column=1, columnspan=2,padx=5, pady=5) self.txt_estimation = ttk.Entry(script_time_frame, width=15, textvariable=self.estimation.script_time, state="readonly") - self.txt_estimation.grid(row=0, column=0) + self.txt_estimation.grid(row=0, column=0,padx=5, pady=5) # Calculate button self.btn_calculate = ttk.Button(grid_frame, text="Calculate", command=self.btn_calculate_click) - self.btn_calculate.grid(row=3, column=1, columnspan=2) + self.btn_calculate.grid(row=3, column=1, columnspan=2,padx=5, pady=5) # Define the Settings update methods diff --git a/MainWindow.py b/MainWindow.py index 60607fa..694ca40 100644 --- a/MainWindow.py +++ b/MainWindow.py @@ -71,21 +71,21 @@ def create_widgets(self): # Settings Panel self.pnl_settings = SettingsPanel(self, inst_info=self.inst_info,settings=self.settings) - self.pnl_settings.grid(row=0, column=0, sticky="ew") + self.pnl_settings.grid(row=0, column=0, sticky="ew",padx=5, pady=5) self.pnl_settings.on_preview_clicked = self.pnl_settings_on_preview_clicked self.pnl_settings.on_write_clicked = self.pnl_settings_on_write_clicked # Estimation Panel self.pnl_estimation = EstimationPanel(self, estimation=self.estimation,settings=self.settings) - self.pnl_estimation.grid(row=1, column=0, sticky="ew") + self.pnl_estimation.grid(row=1, column=0, sticky="ew",padx=5, pady=5) self.pnl_estimation.on_calculate_clicked = self.pnl_estimate_on_calculate_clicked # Buttons button_frame = ttk.Frame(self) - button_frame.grid(row=2, column=0, sticky="w") + button_frame.grid(row=2, column=0, sticky="w",padx=5, pady=5) self.btn_create_new_table = ttk.Button(button_frame, text="Create New Table", command=self.btn_create_new_table_click) - self.btn_create_new_table.grid(row=0, column=0 ) + self.btn_create_new_table.grid(row=0, column=0 ,padx=5, pady=5) self.btn_open_excel = ttk.Button(button_frame, text="Open Excel", command=self.btn_open_excel_click) - self.btn_open_excel.grid(row=0, column=1) + self.btn_open_excel.grid(row=0, column=1,padx=5, pady=5) self.mnu_main = tk.Menu(self.parent) @@ -116,7 +116,7 @@ def update_settings_panel(self): self.pnl_settings.destroy() self.inst_info = InstrumentInfo(settings=self.settings) self.pnl_settings = SettingsPanel(self, inst_info=self.inst_info, settings=self.settings) - self.pnl_settings.grid(row=0, column=0, sticky="ew") + self.pnl_settings.grid(row=0, column=0, sticky="ew",padx=5, pady=5) self.pnl_settings.on_preview_clicked = self.pnl_settings_on_preview_clicked self.pnl_settings.on_write_clicked = self.pnl_settings_on_write_clicked @@ -124,7 +124,7 @@ def update_estimation_panel(self): self.pnl_estimation.destroy() self.inst_info = InstrumentInfo(settings=self.settings) self.pnl_estimation = EstimationPanel(self, estimation=self.estimation,settings=self.settings) - self.pnl_estimation.grid(row=1, column=0, sticky="ew") + self.pnl_estimation.grid(row=1, column=0, sticky="ew",padx=5, pady=5) self.pnl_estimation.on_preview_clicked = self.pnl_settings_on_preview_clicked self.pnl_estimation.on_write_clicked = self.pnl_settings_on_write_clicked self.pnl_estimation.on_calculate_clicked = self.pnl_estimate_on_calculate_clicked diff --git a/SettingsPanel.py b/SettingsPanel.py index 9924c15..3ed405c 100644 --- a/SettingsPanel.py +++ b/SettingsPanel.py @@ -32,14 +32,14 @@ def create_widgets(self): label = ttk.Label(self, text="Settings", font=("Helvetica", 16, "bold")) - label.grid(row=0, column=0, columnspan=2, pady=10) + label.grid(row=0, column=0, columnspan=2, padx=5, pady=5) grid_frame = ttk.Frame(self) - grid_frame.grid(row=1, column=0, columnspan=2, padx=10, pady=10, sticky="nsew") + grid_frame.grid(row=1, column=0, columnspan=2, padx=5, pady=5, sticky="nsew") # Order - ttk.Label(grid_frame, text="Order:").grid(row=0, column=0, sticky=tk.E) + ttk.Label(grid_frame, text="Order:").grid(row=0, column=0, sticky=tk.E,padx=5, pady=5) self.com_order = ttk.Combobox(grid_frame, values=self.order_choices) self.com_order["values"]=self.order_choices @@ -51,7 +51,7 @@ def create_widgets(self): # Do SANS ttk.Label(grid_frame, text="Do SANS:").grid(row=1, column=0, sticky=tk.E) sans_frame = ttk.Frame(grid_frame) - sans_frame.grid(row=1, column=1, columnspan=2) + sans_frame.grid(row=1, column=1, columnspan=2,padx=5, pady=5) self.txt_times_sans = ttk.Entry(sans_frame, width=5) self.txt_times_sans.insert(0, self.settings.num_sans) # Default value self.txt_times_sans.grid(row=0, column=0, padx=5, pady=5) @@ -59,9 +59,9 @@ def create_widgets(self): self.txt_times_sans.bind("", self.update_num_sans) # Do TRANS - ttk.Label(grid_frame, text="Do TRANS:").grid(row=2, column=0, sticky=tk.E) + ttk.Label(grid_frame, text="Do TRANS:").grid(row=2, column=0, sticky=tk.E,padx=5, pady=5) trans_frame = ttk.Frame(grid_frame) - trans_frame.grid(row=2, column=1, columnspan=2) + trans_frame.grid(row=2, column=1, columnspan=2,padx=5, pady=5) self.txt_times_trans = ttk.Entry(trans_frame, width=5) self.txt_times_trans.insert(0, self.settings.num_trans) # Default value self.txt_times_trans.grid(row=0, column=0, padx=5, pady=5) @@ -71,7 +71,7 @@ def create_widgets(self): # Loop over each run self.loop_run_var = tk.BooleanVar(value=self.settings.loop_per_run) # Create BooleanVar and set default value self.chk_loop_run = ttk.Checkbutton(grid_frame, text="Loop over each run?", variable=self.loop_run_var) - self.chk_loop_run.grid(row=3, column=0, columnspan=3, pady=5) + self.chk_loop_run.grid(row=3, column=0, columnspan=3, padx=5, pady=5) self.chk_loop_run.bind("", self.update_loop_per_run) # Collection Mode @@ -83,28 +83,28 @@ def create_widgets(self): # A2 SANS - tk.Label(grid_frame, text="SANS "+self.inst_info.a2_label).grid(row=5, column=0, sticky=tk.E) + tk.Label(grid_frame, text="SANS "+self.inst_info.a2_label).grid(row=5, column=0, sticky=tk.E,padx=5, pady=5) self.com_a2_sans = ttk.Combobox(grid_frame, values=self.inst_info.a2_choices ) self.com_a2_sans.grid(row=5, column=1, columnspan=2, padx=5, pady=5) self.com_a2_sans.set(self.settings.a2_setting_sans) # Set default value self.com_a2_sans.bind("", self.update_a2_sans) # A2 TRANS - tk.Label(grid_frame, text="TRANS "+self.inst_info.a2_label).grid(row=6, column=0, sticky=tk.E) + tk.Label(grid_frame, text="TRANS "+self.inst_info.a2_label).grid(row=6, column=0, sticky=tk.E,padx=5, pady=5) self.com_a2_trans = ttk.Combobox(grid_frame, values=self.inst_info.a2_choices) self.com_a2_trans.grid(row=6, column=1, columnspan=2, padx=5, pady=5) self.com_a2_trans.set(self.settings.a2_setting_trans) # Set default value self.com_a2_trans.bind("", self.update_a2_trans) # Sample Geometry - ttk.Label(grid_frame, text="Sample Geometry:").grid(row=7, column=0, sticky=tk.E) + ttk.Label(grid_frame, text="Sample Geometry:").grid(row=7, column=0, sticky=tk.E,padx=5, pady=5) self.com_sample_geo = ttk.Combobox(grid_frame, values=self.geometry_choices) self.com_sample_geo.grid(row=7, column=1, columnspan=2, padx=5, pady=5) self.com_sample_geo.set(Geometry.get_choice_string(self.settings.geometry)) # Set default value self.com_sample_geo.bind("", self.update_sample_geometry) # Sample Height - ttk.Label(grid_frame, text="Sample Height:").grid(row=8, column=0, sticky=tk.E) + ttk.Label(grid_frame, text="Sample Height:").grid(row=8, column=0, sticky=tk.E,padx=5, pady=5) sample_height_frame = ttk.Frame(grid_frame) sample_height_frame.grid(row=8, column=1, columnspan=2) self.txt_sample_height = ttk.Entry(sample_height_frame, width=5) @@ -114,7 +114,7 @@ def create_widgets(self): self.txt_sample_height.bind("", self.update_sample_height) # Sample Width - ttk.Label(grid_frame, text="Sample Width:").grid(row=9, column=0, sticky=tk.E) + ttk.Label(grid_frame, text="Sample Width:").grid(row=9, column=0, sticky=tk.E,padx=5, pady=5) sample_width_frame = ttk.Frame(grid_frame) sample_width_frame.grid(row=9, column=1, columnspan=2) self.txt_sample_width = ttk.Entry(sample_width_frame, width=5) @@ -125,11 +125,11 @@ def create_widgets(self): # Buttons button_frame = ttk.Frame(self) - button_frame.grid(row=10, column=0, columnspan=2, pady=10) + button_frame.grid(row=10, column=0, columnspan=2,padx=5, pady=5) self.btn_preview_script = ttk.Button(button_frame, text="Preview Script", command=self.btn_preview_script_click) - self.btn_preview_script.grid(row=0, column=0, padx=5) + self.btn_preview_script.grid(row=0, column=0, padx=5, pady=5) self.btn_write_script = ttk.Button(button_frame, text="Write Script", command=self.btn_write_script_click) - self.btn_write_script.grid(row=0, column=1, padx=5) + self.btn_write_script.grid(row=0, column=1,padx=5, pady=5) # Define the Settings update methods def update_order(self, event): From 5a2a1fa61c83d385d6ef27ef211de311e0a62bed Mon Sep 17 00:00:00 2001 From: Dirk Honecker Date: Mon, 6 Jan 2025 02:14:17 +0000 Subject: [PATCH 26/88] saving and opening csv from gui --- MainWindow.py | 41 ++++++-------- TableWidget.py | 151 +++++++++---------------------------------------- 2 files changed, 44 insertions(+), 148 deletions(-) diff --git a/MainWindow.py b/MainWindow.py index 694ca40..ea37508 100644 --- a/MainWindow.py +++ b/MainWindow.py @@ -84,8 +84,8 @@ def create_widgets(self): self.btn_create_new_table = ttk.Button(button_frame, text="Create New Table", command=self.btn_create_new_table_click) self.btn_create_new_table.grid(row=0, column=0 ,padx=5, pady=5) - self.btn_open_excel = ttk.Button(button_frame, text="Open Excel", command=self.btn_open_excel_click) - self.btn_open_excel.grid(row=0, column=1,padx=5, pady=5) + self.btn_open_cvs = ttk.Button(button_frame, text="Open CSV", command=self.btn_open_csv_click) + self.btn_open_cvs.grid(row=0, column=1,padx=5, pady=5) self.mnu_main = tk.Menu(self.parent) @@ -168,38 +168,29 @@ def pnl_estimate_on_calculate_clicked(self): self.estimation.script_time = self.script_generator.estimate_run_time(self.data, self.settings.experiment, self.estimation) def btn_create_new_table_click(self): - self.data.clear() + self.data =None + self.data_frame.reset_table() - def btn_open_excel_click(self): - filepath = filedialog.askopenfilename(defaultextension=".xlsx", filetypes=[("Excel Files", "*.xls;*.xlsx")]) - if filepath: - try: - excel = ExcelIO() - excel.open_excel_workbook(filepath) - exps = excel.extract_experiments() - self.data = exps - except Exception as err: - messagebox.showerror("SANS Script - Error", "Could not open Excel spreadsheet") def mnu_exit_click(self): self.on_close() - def mnu_export_to_excel_click(self): - excel = ExcelIO() - filepath = filedialog.asksaveasfilename(defaultextension=".xls", filetypes=[("Exel File", "*.xls")]) - constructor = TableDataConstructor(TableEntries) - self.data = constructor.create_experiments(self.data_frame.data.values()) - excel.export_data_to_excel(filepath,self.runs) def mnu_save_csv_click(self): - filepath = filedialog.asksaveasfilename(defaultextension=".csv", filetypes=[("CSV File", "*.csv")]) constructor = TableDataConstructor(TableEntries) self.data = constructor.create_experiments(self.data_frame.data.values()) - if filepath: - try: - CsvExporter.export(filepath, self.data) - except Exception as err: - messagebox.showerror("SANS Script - Error", "Could not save as CSV") + + try: + self.data_frame.table.exportTable() + except Exception as err: + messagebox.showerror("SANS Script - Error", "Could not save as CSV") + + def btn_open_csv_click(self): + + try: + self.data_frame.table.importTable() + except Exception as err: + messagebox.showerror("SANS Script - Error", f"Could not open CS") def on_close(self): # Save last_instrument to settings diff --git a/TableWidget.py b/TableWidget.py index 9496e8e..66017e6 100644 --- a/TableWidget.py +++ b/TableWidget.py @@ -113,16 +113,6 @@ def show(self, callback=None): self.parentframe.columnconfigure(1,weight=1) # Configure grid layout - ''' - #self.parentframe.grid_rowconfigure(0, weight=0) - #self.parentframe.grid_rowconfigure(1, weight=1) - #self.parentframe.grid_rowconfigure(2, weight=0) - - #self.parentframe.grid_columnconfigure(0, weight=0) - #self.parentframe.grid_columnconfigure(1, weight=1) - #self.parentframe.grid_columnconfigure(2, weight=0) - #self.parentframe.grid_columnconfigure(3, weight=0) - ''' self.tablecolheader.grid(row=0,column=1,rowspan=1,sticky='news',pady=0,ipady=0) self.tablerowheader.grid(row=1,column=0,rowspan=1,sticky='news',pady=0,ipady=0) @@ -140,114 +130,7 @@ def show(self, callback=None): self.tablecolheader.xview("moveto", 0) self.xview("moveto", 0) return - ''' - def on_resize(self, event): - self.update_table_size() - - def update_table_size(self): - """Adjust the table width based on the available space in the parent frame.""" - available_width= self.parentframe.winfo_width() - if available_width <= 1.0: - available_width = self.parentframe.master.winfo_width() - print(available_width) - required_width = (self.cellwidth) * self.cols - self.tablewidth = max(available_width, required_width) - self.redrawTable() - ''' - ''' - def updateModel(self, model): - """Call this method to update the table model""" - - self.model = model - self.rows = self.model.getRowCount() - self.cols = self.model.getColumnCount() - if self.tablewidth is None: - self.tablewidth = (self.cellwidth)*self.cols - self.tablecolheader = CustomColumnHeader(self.parentframe, self) - self.tablerowheader = CustomRowHeader(self.parentframe, self) - self.createTableFrame() - return - ''' - ''' - def redrawTable(self, event=None, callback=None): - self.redrawVisible(event, callback) - return - ''' - ''' - def getVisibleRegion(self): - """Get visible region of table to display""" - - x1, y1 = self.canvasx(0), self.canvasy(0) - #w, h = self.winfo_width(), self.winfo_height() - w,h= self.parentframe.winfo_width(), self.parentframe.winfo_height() - if w <= 1.0 or h <= 1.0: - w, h = self.parentframe.master.winfo_width(), self.parentframe.master.winfo_height() - x2, y2 = self.canvasx(w), self.canvasy(h) - return x1, y1, x2, y2 - - def redrawVisible(self, event=None, callback=None): - """Redraw the visible portion of the canvas""" - - model = self.model - self.rows = self.model.getRowCount() - self.cols = self.model.getColumnCount() - if self.tablewidth is None: - self.tablewidth = (self.cellwidth) * self.cols - self.configure(bg=self.bgcolor) - self.setColPositions() - - #are we drawing a filtered subset of the recs? - if self.filtered == True and self.model.filteredrecs != None: - self.rows = len(self.model.filteredrecs) - self.delete('colrect') - - self.rowrange = range(0,self.rows) - self.configure(scrollregion=(0,0, self.tablewidth+self.x_start, - self.rowheight*self.rows+10)) - - x1, y1, x2, y2 = self.getVisibleRegion() - startvisiblerow, endvisiblerow = self.getVisibleRows(y1, y2) - self.visiblerows = range(startvisiblerow, endvisiblerow) - startvisiblecol, endvisiblecol = self.getVisibleCols(x1, x2) - self.visiblecols = range(startvisiblecol, endvisiblecol) - - if self.cols == 0 or self.rows == 0: - self.delete('entry') - self.delete('rowrect') - self.delete('currentrect') - self.delete('gridline','text') - self.tablerowheader.redraw() - return - self.drawGrid(startvisiblerow, endvisiblerow) - align = self.align - self.delete('fillrect') - for row in self.visiblerows: - if callback != None: - callback() - for col in self.visiblecols: - colname = model.getColumnName(col) - bgcolor = model.getColorAt(row,col, 'bg') - fgcolor = model.getColorAt(row,col, 'fg') - text = model.getValueAt(row,col) - self.drawText(row, col, text, fgcolor, align) - if bgcolor != None: - self.drawRect(row,col, color=bgcolor) - - #self.drawSelectedCol() - self.tablecolheader.redraw() - self.tablerowheader.redraw(align=self.align, showkeys=self.showkeynamesinheader) - #self.setSelectedRow(self.currentrow) - self.drawSelectedRow() - self.drawSelectedRect(self.currentrow, self.currentcol) - #print self.multiplerowlist - - if len(self.multiplerowlist)>1: - self.tablerowheader.drawSelectedRows(self.multiplerowlist) - self.drawMultipleRows(self.multiplerowlist) - self.drawMultipleCells() - return - ''' def deleteCells(self, rows=None, cols=None): """Clear the cell contents""" @@ -264,14 +147,32 @@ def deleteCells(self, rows=None, cols=None): self.redrawCell(row,col) return + def clearData(self, evt=None): - """Delete cells from gui event""" + """Delete all cells in the table.""" + n = messagebox.askyesno("Clear Confirm", "Clear all data?", parent=self.parentframe) + if not n: + return + + # Get all rows and columns + rows = range(self.model.getRowCount()) + cols = range(self.model.getColumnCount()) + + # Delete all cells + for col in cols: + for row in rows: + self.model.deleteCellRecord(row, col) - rows = self.multiplerowlist - cols = self.multiplecollist - self.deleteCells(rows, cols) + self.redrawTable() return + def new(self, evt=None): + """Clears all the data and makes a new table""" + constructor = TableDataConstructor(TableEntries) + EMPTY_DATA = constructor.construct_empty_data() + self.model.importDict(EMPTY_DATA) + self.redrawTable() + def fillDown(self, rowlist, collist): """Fill down a column, or multiple columns""" model = self.model @@ -381,7 +282,7 @@ def popupMenu(self, event, rows=None, cols=None, outside=None): "Delete Row(s)": lambda: self.deleteRow(), "Clear Data": lambda: self.clearData(), "Select All": self.select_All, - "New": self.new, + "New": lambda: self.new(), "Import Table": self.importTable, "Export Table": self.exportTable, "Export Reductionbatch": self.exportTable, @@ -402,6 +303,8 @@ def createSubMenu(parent, label, commands): menu.add_command(label=action, command=defaultactions[action]) return menu + + def add_commands(fieldtype): """Add commands to popup menu for column type and specific cell""" functions = self.columnactions[fieldtype] @@ -481,7 +384,9 @@ def customOption(self): print("Custom option selected") return - + def reset_table(self): + """Reset the table to its initial empty state.""" + self.table.new() From 27f07d6e3d2683faac2ff326cdf87432e4773469 Mon Sep 17 00:00:00 2001 From: Dirk Honecker Date: Mon, 6 Jan 2025 11:16:27 +0000 Subject: [PATCH 27/88] Clean reset of table when loading of new data --- MainWindow.py | 6 +++--- Scripting/ScriptGenerator.py | 8 ++++---- TableWidget.py | 28 ++++++++++++++++++++++++++-- 3 files changed, 33 insertions(+), 9 deletions(-) diff --git a/MainWindow.py b/MainWindow.py index ea37508..fc209eb 100644 --- a/MainWindow.py +++ b/MainWindow.py @@ -142,19 +142,19 @@ def load_last_instrument(self): def pnl_settings_on_write_clicked(self): - filetypes = [("genie_python Files", "*.py")] if self.settings.is_instrument in ["LARMOR", "LOQ", "ZOOM", "SANS2D"] else [("Open Genie Files", "*.gcl")] + filetypes = [("genie_python Files", "*.py")] if self.settings.experiment.is_instrument in ["LARMOR", "LOQ", "ZOOM", "SANS2D"] else [("Open Genie Files", "*.gcl")] filepath = filedialog.asksaveasfilename(defaultextension=filetypes[0][1], filetypes=filetypes) constructor = TableDataConstructor(TableEntries) self.data = constructor.create_experiments(self.data_frame.data.values()) if filepath: scriptname = filepath.split("/")[-1].replace(" ", "_").rsplit(".", 1)[0] - script_generator= ScriptGenerator(self.settings.is_instrument) + script_generator= ScriptGenerator(self.settings.experiment.is_instrument) script = script_generator.generate_script(self.data, self.settings.experiment, scriptname) with open(filepath, "w") as file: file.write(script) def pnl_settings_on_preview_clicked(self): - self.script_generator= ScriptGenerator(self.settings.is_instrument) + self.script_generator= ScriptGenerator(self.settings.experiment.is_instrument) constructor = TableDataConstructor(TableEntries) self.data = constructor.create_experiments(self.data_frame.data.values()) self.script = self.script_generator.generate_script(self.data, self.settings.experiment, "my_script") diff --git a/Scripting/ScriptGenerator.py b/Scripting/ScriptGenerator.py index 4e2649f..a30618d 100644 --- a/Scripting/ScriptGenerator.py +++ b/Scripting/ScriptGenerator.py @@ -159,13 +159,13 @@ def do_sans_run(self, script, exp, indent, settings): def aperture_string(self, size): - if size in [A2Setting.Large, A2Setting.Larmor]: + if size in [A2Setting.LARGE, A2Setting.LARMOR_LARGE]: return "large" - elif size in [A2Setting.Medium, A2Setting.Larmor]: + elif size in [A2Setting.MEDIUM, A2Setting.LARMOR_MEDIUM]: return "medium" - elif size in [A2Setting.Small, A2Setting.Larmor]: + elif size in [A2Setting.SMALL, A2Setting.LARMOR_SMALL]: return "small" - elif size in [A2Setting.XLarge, A2Setting.Larmor]: + elif size in [A2Setting.X_LARGE, A2Setting.Larmor]: return "xlarge" return "" diff --git a/TableWidget.py b/TableWidget.py index 66017e6..8ff0986 100644 --- a/TableWidget.py +++ b/TableWidget.py @@ -85,8 +85,16 @@ def ImportTableData(table, sep=None): return None model = table.getModel() + #lift focus from cell and row for clean delete + table.clearSelected() model.deleteRows() + table.data = dictdata model.importDict(dictdata) + #reset focus of selected row + table.drawCellEntry(1, table.currentcol) + table.setSelectedRow(1) + table.drawSelectedRect(1, table.currentcol) + table.drawSelectedRow() table.redrawTable() return dictdata @@ -169,9 +177,19 @@ def clearData(self, evt=None): def new(self, evt=None): """Clears all the data and makes a new table""" constructor = TableDataConstructor(TableEntries) - EMPTY_DATA = constructor.construct_empty_data() + EMPTY_DATA = constructor.construct_empty_data() + self.data = EMPTY_DATA + self.model.deleteRows() + self.model.importDict(EMPTY_DATA) + #reset focus of selected row + self.drawCellEntry(1, self.currentcol) + self.setSelectedRow(1) + self.drawSelectedRect(1, self.currentcol) + self.drawSelectedRow() self.redrawTable() + + def fillDown(self, rowlist, collist): """Fill down a column, or multiple columns""" @@ -265,7 +283,13 @@ def move_to_next_row(self, event): self.drawSelectedRow() coltype = self.model.getColumnType(self.currentcol) if coltype == "text" or coltype == "number": - self.drawCellEntry(self.currentrow, self.currentcol) + if self.currentrow < self.rows: + self.drawCellEntry(self.currentrow, self.currentcol) + else: + self.drawCellEntry(self.rows-1, self.currentcol) + self.setSelectedRow(self.rows-1) + self.drawSelectedRect(self.currentrow, self.currentcol) + self.drawSelectedRow() def popupMenu(self, event, rows=None, cols=None, outside=None): """Add left and right click behaviour for canvas, should not have to override From 9a0daf0ff6baf83d987ef9318e63b032cd771c63 Mon Sep 17 00:00:00 2001 From: Dirk Honecker Date: Mon, 6 Jan 2025 11:24:00 +0000 Subject: [PATCH 28/88] Setup clean new table --- TableWidget.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/TableWidget.py b/TableWidget.py index 8ff0986..2ae1219 100644 --- a/TableWidget.py +++ b/TableWidget.py @@ -178,14 +178,16 @@ def new(self, evt=None): """Clears all the data and makes a new table""" constructor = TableDataConstructor(TableEntries) EMPTY_DATA = constructor.construct_empty_data() + #clear focus of selected row + self.clearSelected() self.data = EMPTY_DATA self.model.deleteRows() self.model.importDict(EMPTY_DATA) #reset focus of selected row - self.drawCellEntry(1, self.currentcol) - self.setSelectedRow(1) - self.drawSelectedRect(1, self.currentcol) + self.drawCellEntry(0, self.currentcol) + self.setSelectedRow(0) + self.drawSelectedRect(0, self.currentcol) self.drawSelectedRow() self.redrawTable() From a9120400e011770af7a9625d7261140d79e2738e Mon Sep 17 00:00:00 2001 From: Dirk Honecker Date: Mon, 6 Jan 2025 11:42:24 +0000 Subject: [PATCH 29/88] Import export table in Gui/Menu and unify naming --- MainWindow.py | 10 ++++++---- TableDataConstructor.py | 4 ++-- TableWidget.py | 2 +- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/MainWindow.py b/MainWindow.py index fc209eb..7331325 100644 --- a/MainWindow.py +++ b/MainWindow.py @@ -80,13 +80,14 @@ def create_widgets(self): self.pnl_estimation.on_calculate_clicked = self.pnl_estimate_on_calculate_clicked # Buttons button_frame = ttk.Frame(self) - button_frame.grid(row=2, column=0, sticky="w",padx=5, pady=5) + button_frame.grid(row=3, column=0, sticky="w",padx=5, pady=5) self.btn_create_new_table = ttk.Button(button_frame, text="Create New Table", command=self.btn_create_new_table_click) self.btn_create_new_table.grid(row=0, column=0 ,padx=5, pady=5) - self.btn_open_cvs = ttk.Button(button_frame, text="Open CSV", command=self.btn_open_csv_click) + self.btn_open_cvs = ttk.Button(button_frame, text="Import Table", command=self.btn_open_csv_click) self.btn_open_cvs.grid(row=0, column=1,padx=5, pady=5) - + self.btn_save_csv = ttk.Button(button_frame, text="Export Table", command=self.mnu_save_csv_click) + self.btn_save_csv.grid(row=0, column=2,padx=5, pady=5) self.mnu_main = tk.Menu(self.parent) self.parent.config(menu=self.mnu_main) @@ -94,7 +95,8 @@ def create_widgets(self): file_menu = tk.Menu(self.mnu_main, tearoff=0) self.mnu_main.add_cascade(label="File", menu=file_menu) #file_menu.add_command(label="Export to Excel", command=self.mnu_export_to_excel_click) - file_menu.add_command(label="Save as CSV", command=self.mnu_save_csv_click) + file_menu.add_command(label="Import Table", command=self.btn_open_csv_click) + file_menu.add_command(label="Export Table", command=self.mnu_save_csv_click) file_menu.add_separator() file_menu.add_command(label="Exit", command=self.mnu_exit_click) diff --git a/TableDataConstructor.py b/TableDataConstructor.py index d6bc8b0..db93395 100644 --- a/TableDataConstructor.py +++ b/TableDataConstructor.py @@ -7,14 +7,14 @@ def __init__(self, table_entries): def construct_empty_data(self): empty_data = { - "rec1": {entry: "" for entry in self.table_entries} + 0: {entry: "" for entry in self.table_entries} } return empty_data def construct_data(self, data_list): data = {} for i, row in enumerate(data_list, start=1): - data[f"rec{i}"] = {entry: value for entry, value in zip(self.table_entries, row)} + data[i] = {entry: value for entry, value in zip(self.table_entries, row)} return data def deconstruct_data(self, data_dict): diff --git a/TableWidget.py b/TableWidget.py index 2ae1219..253837b 100644 --- a/TableWidget.py +++ b/TableWidget.py @@ -308,7 +308,7 @@ def popupMenu(self, event, rows=None, cols=None, outside=None): "Delete Row(s)": lambda: self.deleteRow(), "Clear Data": lambda: self.clearData(), "Select All": self.select_All, - "New": lambda: self.new(), + "Create New Table": lambda: self.new(), "Import Table": self.importTable, "Export Table": self.exportTable, "Export Reductionbatch": self.exportTable, From ae1cec0e0b777cd05c45e78613902c7a75f919fd Mon Sep 17 00:00:00 2001 From: Dirk Honecker Date: Mon, 6 Jan 2025 15:29:03 +0000 Subject: [PATCH 30/88] Pass loaded table on to scripts by setting up data at the right places --- TableWidget.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/TableWidget.py b/TableWidget.py index 253837b..20898ee 100644 --- a/TableWidget.py +++ b/TableWidget.py @@ -90,6 +90,8 @@ def ImportTableData(table, sep=None): model.deleteRows() table.data = dictdata model.importDict(dictdata) + # Update the data in the TableWidget instance + table.parentframe.data = dictdata #reset focus of selected row table.drawCellEntry(1, table.currentcol) table.setSelectedRow(1) @@ -215,8 +217,8 @@ def display_sheet_data(self,name=None): sheet = self.workbook[sheet_name] #TableEntries = "col1, col2, label, col3, col4, label2, col5, col6, label3" constructor = TableDataConstructor(TableEntries) - data = constructor.construct_data(sheet.iter_rows(values_only=True)) - self.data = data + self.data = constructor.construct_data(sheet.iter_rows(values_only=True)) + self.model.importDict(data) self.redraw() @@ -389,7 +391,7 @@ def __init__(self, parent=None, data=None): if data is None: data = TableWidget.EMPTY_DATA self.data = data - self.table = CustomTableCanvas(self, data=self.data) + self.table = CustomTableCanvas(self, data=self.data ) #table.importCSV("test.csv") print (self.table.model.columnNames) #table.model.data[1]["a"] = "XX" From 4adefe54d373387755c484a4a09142e76457db07 Mon Sep 17 00:00:00 2001 From: Dirk Honecker Date: Mon, 6 Jan 2025 18:06:58 +0000 Subject: [PATCH 31/88] Rescale Preview window when expanding and propagate settings to script --- Dialogs/PreviewDialog.py | 10 ++++- MainWindow.py | 26 +++++------ Models/A2Setting.py | 16 +++---- Models/ExperimentSettings.py | 24 +++++----- Models/Geometry.py | 8 ++-- Models/InstrumentInfo.py | 20 +++++---- Properties/Settings.py | 6 +-- Scripting/ScriptGenerator.py | 87 +++++++++++++++++++----------------- SettingsPanel.py | 4 +- 9 files changed, 106 insertions(+), 95 deletions(-) diff --git a/Dialogs/PreviewDialog.py b/Dialogs/PreviewDialog.py index f822d55..5cf56cc 100644 --- a/Dialogs/PreviewDialog.py +++ b/Dialogs/PreviewDialog.py @@ -9,10 +9,14 @@ def __init__(self, parent, script=None): self.parent = parent # Store reference to the parent (main window) self.title("PreviewDialog") - self.geometry("600x500") + #self.geometry("600x500") + self.resizable(True, True) + self.columnconfigure(0,weight=1) + self.rowconfigure(0,weight=1) self.grid_rowconfigure(0, weight=1) self.grid_columnconfigure(0, weight=1) + self.rowconfigure(0, weight=1) # Create a frame for the text area with a border border_frame = tk.Frame(self, borderwidth=1, relief="solid") @@ -20,7 +24,9 @@ def __init__(self, parent, script=None): # Create a scrollable text area self.txt_script = scrolledtext.ScrolledText(border_frame, wrap=tk.WORD) - self.txt_script.grid(row=0, column=0, sticky="nsew",padx=5, pady=5) + self.txt_script.grid(row=0, column=0, sticky="nsew", padx=5, pady=5) + border_frame.grid_rowconfigure(0, weight=1) + border_frame.grid_columnconfigure(0, weight=1) self.txt_script.insert(tk.END, script) self.txt_script.config(state=tk.DISABLED) diff --git a/MainWindow.py b/MainWindow.py index 7331325..397f66d 100644 --- a/MainWindow.py +++ b/MainWindow.py @@ -29,8 +29,8 @@ def __init__(self,parent): self.settings = Settings() self.estimation = Estimation(settings=self.settings) - self.settings.experiment = ExperimentSettings(settings=self.settings, estimation=self.estimation) - self.inst_info = InstrumentInfo(settings=self.settings) + self.experiment = ExperimentSettings(settings=self.settings, estimation=self.estimation) + self.inst_info = InstrumentInfo(settings=self.settings,experiment=self.experiment) self.create_widgets() self.load_last_instrument() @@ -103,10 +103,10 @@ def create_widgets(self): instrument_menu = tk.Menu(self.mnu_main, tearoff=0) self.mnu_main.add_cascade(label="Instrument", menu=instrument_menu) - instrument_menu.add_radiobutton(label="LARMOR", variable=self.settings.experiment.instrument, value="LARMOR", command=self.update_panel) - instrument_menu.add_radiobutton(label="LOQ", variable=self.settings.experiment.instrument, value="LOQ", command=self.update_panel) - instrument_menu.add_radiobutton(label="SANS2D", variable=self.settings.experiment.instrument, value="SANS2D", command=self.update_panel) - instrument_menu.add_radiobutton(label="ZOOM", variable=self.settings.experiment.instrument, value="ZOOM", command=self.update_panel) + instrument_menu.add_radiobutton(label="LARMOR", variable=self.experiment.instrument, value="LARMOR", command=self.update_panel) + instrument_menu.add_radiobutton(label="LOQ", variable=self.experiment.instrument, value="LOQ", command=self.update_panel) + instrument_menu.add_radiobutton(label="SANS2D", variable=self.experiment.instrument, value="SANS2D", command=self.update_panel) + instrument_menu.add_radiobutton(label="ZOOM", variable=self.experiment.instrument, value="ZOOM", command=self.update_panel) """ @@ -138,28 +138,28 @@ def update_panel(self): def load_last_instrument(self): inst = self.settings.last_instrument # Replace with actual settings retrieval - self.settings.experiment.is_instrument = inst + self.experiment.is_instrument = inst def pnl_settings_on_write_clicked(self): - filetypes = [("genie_python Files", "*.py")] if self.settings.experiment.is_instrument in ["LARMOR", "LOQ", "ZOOM", "SANS2D"] else [("Open Genie Files", "*.gcl")] + filetypes = [("genie_python Files", "*.py")] if self.experiment.is_instrument in ["LARMOR", "LOQ", "ZOOM", "SANS2D"] else [("Open Genie Files", "*.gcl")] filepath = filedialog.asksaveasfilename(defaultextension=filetypes[0][1], filetypes=filetypes) constructor = TableDataConstructor(TableEntries) self.data = constructor.create_experiments(self.data_frame.data.values()) if filepath: scriptname = filepath.split("/")[-1].replace(" ", "_").rsplit(".", 1)[0] - script_generator= ScriptGenerator(self.settings.experiment.is_instrument) - script = script_generator.generate_script(self.data, self.settings.experiment, scriptname) + script_generator= ScriptGenerator(self.settings) + script = script_generator.generate_script(self.data, scriptname) with open(filepath, "w") as file: file.write(script) def pnl_settings_on_preview_clicked(self): - self.script_generator= ScriptGenerator(self.settings.experiment.is_instrument) + self.script_generator= ScriptGenerator(self.settings) constructor = TableDataConstructor(TableEntries) self.data = constructor.create_experiments(self.data_frame.data.values()) - self.script = self.script_generator.generate_script(self.data, self.settings.experiment, "my_script") + self.script = self.script_generator.generate_script(self.data, "my_script") print(self.script) preview_dialog = PreviewDialog(self, self.script) preview_dialog.show() @@ -167,7 +167,7 @@ def pnl_settings_on_preview_clicked(self): def pnl_estimate_on_calculate_clicked(self): constructor = TableDataConstructor(TableEntries) self.data = constructor.create_experiments(self.data_frame.data.values()) - self.estimation.script_time = self.script_generator.estimate_run_time(self.data, self.settings.experiment, self.estimation) + self.estimation.script_time = self.script_generator.estimate_run_time(self.data, self.estimation) def btn_create_new_table_click(self): self.data =None diff --git a/Models/A2Setting.py b/Models/A2Setting.py index 9533cac..740b756 100644 --- a/Models/A2Setting.py +++ b/Models/A2Setting.py @@ -1,12 +1,12 @@ class A2Setting: - X_LARGE = "XLarge = 20x18 mm" - LARGE = "Large = 17 mm" - MEDIUM = "Medium = 12 mm" - SMALL = "Small = 5/1 mm" - TRANS = "Trans = 1 mm" - LARMOR_LARGE = "Large = 25, 16 mm" - LARMOR_MEDIUM = "Medium = 20, 14 mm" - LARMOR_SMALL = "Small = 15, 10 mm" + X_LARGE = "XLarge (20x18 mm)" + LARGE = "Large (17 mm)" + MEDIUM = "Medium (12 mm)" + SMALL = "Small (5/1 mm)" + TRANS = "Trans (1 mm)" + LARMOR_LARGE = "Large (25, 16 mm)" + LARMOR_MEDIUM = "Medium (20, 14 mm)" + LARMOR_SMALL = "Small (15, 10 mm)" LARMOR_choices = [ diff --git a/Models/ExperimentSettings.py b/Models/ExperimentSettings.py index 1b08735..490a3af 100644 --- a/Models/ExperimentSettings.py +++ b/Models/ExperimentSettings.py @@ -2,7 +2,6 @@ from Properties.Settings import Settings from Models.A2Setting import A2Setting from Models.DataCollection import DataCollection -from Models.InstrumentInfo import InstrumentInfo from Models.Estimation import Estimation from Properties.Settings import Settings from Models.RunOrder import RunOrder @@ -11,16 +10,17 @@ class ExperimentSettings: def __init__(self, settings=None, estimation=None): self.settings = settings if settings is not None else Settings() - self.estimation = estimation if estimation is not None else Estimation(settings=self.settings) + self.estimation = estimation if estimation is not None else Estimation(settings=self.settings) + - self._order = tk.StringVar(value=RunOrder.get_choice_string(self.settings.run_order)) + self._run_order = tk.StringVar(value=RunOrder.get_choice_string(self.settings.run_order)) self._num_sans = tk.IntVar(value=self.settings.num_sans) self._num_trans = tk.IntVar(value=self.settings.num_trans) self._loop_per_run = tk.BooleanVar(value=self.settings.loop_per_run) self._a2_setting_sans = tk.StringVar(value=self.settings.a2_setting_sans) self._a2_setting_trans = tk.StringVar(value=self.settings.a2_setting_trans) - self._sample_geometry = tk.StringVar(value=Geometry.get_choice_string(self.settings.geometry)) + self._sample_geometry = tk.StringVar(value=Geometry.get_choice_string(self.settings.sample_geometry)) self._sample_height = tk.DoubleVar(value=self.settings.sample_height) self._sample_width = tk.DoubleVar(value=self.settings.sample_width) self._sample_width_enabled = tk.BooleanVar(value=True) @@ -33,7 +33,7 @@ def __init__(self, settings=None, estimation=None): self.instrument.trace_add("write", self.on_property_changed) self.instrument.trace_add("write", self.update_instrument) - self._order.trace_add("write", self.on_property_changed) + self._run_order.trace_add("write", self.on_property_changed) self._num_sans.trace_add("write", self.on_property_changed) self._num_trans.trace_add("write", self.on_property_changed) self._loop_per_run.trace_add("write", self.on_property_changed) @@ -51,7 +51,7 @@ def __init__(self, settings=None, estimation=None): self._loop_per_run.trace_add("write", self.validate) - self._order.trace_add("write", self.save_changes) + self._run_order.trace_add("write", self.save_changes) self._num_sans.trace_add("write", self.save_changes) self._num_trans.trace_add("write", self.save_changes) self._loop_per_run.trace_add("write", self.save_changes) @@ -142,25 +142,25 @@ def set_instrument(self, instrument, a2_label, a2_choices, data_collection_label def save_changes(self, *args): - self.settings.run_order = self.order + self.settings.run_order = self.run_order self.settings.num_sans = self.num_sans self.settings.num_trans = self.num_trans self.settings.loop_per_run = self.loop_per_run self.settings.a2_setting_sans = self.a2_setting_sans self.settings.a2_setting_trans = self.a2_setting_trans - self.settings.geometry = self.sample_geometry + self.settings.sample_geometry = self.sample_geometry self.settings.sample_height = self.sample_height self.settings.sample_width = self.sample_width self.settings.collection_mode = self.collection_mode @property - def order(self): + def run_order(self): return self._order.get() - @order.setter - def order(self, value): - self._order.set(value) + @run_order.setter + def run_order(self, value): + self._run_order.set(value) @property def num_sans(self): diff --git a/Models/Geometry.py b/Models/Geometry.py index b8c2269..966004a 100644 --- a/Models/Geometry.py +++ b/Models/Geometry.py @@ -1,8 +1,8 @@ class Geometry: - DISC = [1, "1: Disc"] - CYLINDRICAL = [2, "2: Cylindrical"] - FLAT_PLATE = [3, "3: Flat Plate"] - SINGLE_CRYSTAL = [4, "4: Single Crystal"] + DISC = [1, "Disc"] + CYLINDRICAL = [2, "Cylindrical"] + FLAT_PLATE = [3, "Flat Plate"] + SINGLE_CRYSTAL = [4, "Single Crystal"] choices = [ diff --git a/Models/InstrumentInfo.py b/Models/InstrumentInfo.py index 896ba05..6ad59ee 100644 --- a/Models/InstrumentInfo.py +++ b/Models/InstrumentInfo.py @@ -1,7 +1,8 @@ import tkinter as tk from tkinter import StringVar, BooleanVar from typing import List -from Properties.Settings import Settings +#from Properties.Settings import Settings +from Models.ExperimentSettings import ExperimentSettings class InstrumentInfo: property_changed = [] @@ -14,14 +15,15 @@ class InstrumentInfo: a2_trans_enabled = False collection_mode_enabled = True - def __init__(self,settings=None): - self.settings = settings if settings is not None else Settings() - self._a2_label = StringVar(value=self.settings.experiment.a2_label) - self._a2_choices = tk.Variable(value=self.settings.experiment.a2_choices) - self._data_collection_label = StringVar(value=self.settings.experiment.data_collection_label) - self._data_collection_choices = tk.Variable(value=self.settings.experiment.data_collection_choices) - self._a2_trans_enabled = BooleanVar(value=self.settings.experiment.a2_trans_enabled) - self._collection_mode_enabled = BooleanVar(value=self.settings.experiment.collection_mode_enabled) + def __init__(self,settings=None, experiment=None): + #self.settings = settings if settings is not None else Settings() + self.experiment = experiment if experiment is not None else ExperimentSettings() + self._a2_label = StringVar(value=self.experiment.a2_label) + self._a2_choices = tk.Variable(value=self.experiment.a2_choices) + self._data_collection_label = StringVar(value=self.experiment.data_collection_label) + self._data_collection_choices = tk.Variable(value=self.experiment.data_collection_choices) + self._a2_trans_enabled = BooleanVar(value=self.experiment.a2_trans_enabled) + self._collection_mode_enabled = BooleanVar(value=self.experiment.collection_mode_enabled) self._script_generator = None diff --git a/Properties/Settings.py b/Properties/Settings.py index 1f80c4d..5bda93c 100644 --- a/Properties/Settings.py +++ b/Properties/Settings.py @@ -38,11 +38,11 @@ def sample_width(self, value): self.save() @property - def geometry(self): + def sample_geometry(self): return self.dict_settings.get("DEFAULT","Geometry") - @geometry.setter - def geometry(self, value): + @sample_geometry.setter + def sample_geometry(self, value): self.dict_settings.set("DEFAULT","Geometry", str(value)) self.save() diff --git a/Scripting/ScriptGenerator.py b/Scripting/ScriptGenerator.py index a30618d..aa69856 100644 --- a/Scripting/ScriptGenerator.py +++ b/Scripting/ScriptGenerator.py @@ -3,6 +3,7 @@ from datetime import datetime, timedelta from Models.Estimation import Estimation from Models.Experiment import Experiment +from Properties.Settings import Settings from Models.ExperimentSettings import ExperimentSettings from Models.A2Setting import A2Setting from Models.RunOrder import RunOrder @@ -10,16 +11,18 @@ class ScriptGenerator: - def __init__(self, instrument=None): + def __init__(self, settings=None): + self.settings = settings if settings is not None else Settings() + #self.experiment = experiment if experiment is not None else ExperimentSettings() self.indent = " " - self.instrument = instrument + self.instrument = self.settings.last_instrument - def generate_script(self, runs, settings, scriptname): + def generate_script(self, runs, scriptname): script = f"# Script created by SANSScript at {datetime.now()}\n\n" script += self.generate_header(scriptname) - script += self.generate_setup(settings) - script += self.generate_runs(runs, settings) + script += self.generate_setup() + script += self.generate_runs(runs) script += "\n" return script @@ -43,11 +46,11 @@ def generate_header(self, scriptname): script += f"\ndef {scriptname}():\n" return script - def generate_setup(self, settings): + def generate_setup(self): script = "" - script += self.get_indent_string(1) + f"g.change_sample_par('Width', '{settings.sample_width}')\n" - script += self.get_indent_string(1) + f"g.change_sample_par('height', '{settings.sample_height}')\n" - script += self.get_indent_string(1) + f"g.change_sample_par('Geometry', '{settings.sample_geometry}')\n\n" + script += self.get_indent_string(1) + f"g.change_sample_par('Width', '{self.settings.sample_width}')\n" + script += self.get_indent_string(1) + f"g.change_sample_par('height', '{self.settings.sample_height}')\n" + script += self.get_indent_string(1) + f"g.change_sample_par('Geometry', '{self.settings.sample_geometry}')\n\n" return script def check_positions(self, runs): @@ -60,61 +63,61 @@ def check_positions(self, runs): script += self.get_indent_string(2) + "return\n\n" return script - def generate_runs(self, runs, settings): + def generate_runs(self, runs): script = self.check_positions(runs) if not runs: return script - if float(settings.settings.run_order) == RunOrder.ALL_TRANS[0]: - script += self.get_indent_string(1) + f"for i in range({settings.num_trans}):\n" + if float(self.settings.run_order) == RunOrder.ALL_TRANS[0]: + script += self.get_indent_string(1) + f"for i in range({self.settings.num_trans}):\n" for exp in runs: if exp.trans: - script = self.do_trans_run(script, exp, 2, settings) - script += self.get_indent_string(1) + f"for i in range({settings.num_sans}):\n" + script = self.do_trans_run(script, exp, 2) + script += self.get_indent_string(1) + f"for i in range({self.settings.num_sans}):\n" for exp in runs: if exp.sans: - script = self.do_sans_run(script, exp, 2, settings) - elif float(settings.settings.run_order) == RunOrder.ALL_SANS[0]: - script += self.get_indent_string(1) + f"for i in range({settings.num_sans}):\n" + script = self.do_sans_run(script, exp, 2) + elif float(self.settings.run_order) == RunOrder.ALL_SANS[0]: + script += self.get_indent_string(1) + f"for i in range({self.settings.num_sans}):\n" for exp in runs: if exp.sans: - script = self.do_sans_run(script, exp, 2, settings) - if settings.num_trans and any(exp.trans for exp in runs): - script += self.get_indent_string(1) + f"for i in range({settings.num_trans}):\n" + script = self.do_sans_run(script, exp, 2) + if self.settings.num_trans and any(exp.trans for exp in runs): + script += self.get_indent_string(1) + f"for i in range({self.settings.num_trans}):\n" for exp in runs: if exp.trans: - script = self.do_trans_run(script, exp, 2, settings) + script = self.do_trans_run(script, exp, 2) else: - script += self.get_indent_string(1) + f"num_sans = {settings.num_sans}\n" - script += self.get_indent_string(1) + f"num_trans = {settings.num_trans}\n" + script += self.get_indent_string(1) + f"num_sans = {self.settings.num_sans}\n" + script += self.get_indent_string(1) + f"num_trans = {self.settings.num_trans}\n" script += self.get_indent_string(1) + "count = 0\n" script += self.get_indent_string(1) + "while True:\n" indent = 2 for exp in runs: - if float(settings.settings.run_order) == RunOrder.TRANS_FIRST[0]: + if float(self.settings.run_order) == RunOrder.TRANS_FIRST[0]: if exp.trans: script += self.get_indent_string(indent) + "if count < num_trans:\n" - script = self.do_trans_run(script, exp, indent + 1, settings) + script = self.do_trans_run(script, exp, indent + 1) if exp.sans: script += self.get_indent_string(indent) + "if count < num_sans:\n" - script = self.do_sans_run(script, exp, indent + 1, settings) + script = self.do_sans_run(script, exp, indent + 1) else: if exp.sans: script += self.get_indent_string(indent) + "if count < num_sans:\n" - script = self.do_sans_run(script, exp, indent + 1, settings) + script = self.do_sans_run(script, exp, indent + 1) if exp.trans: script += self.get_indent_string(indent) + "if count < num_trans:\n" - script = self.do_trans_run(script, exp, indent + 1, settings) + script = self.do_trans_run(script, exp, indent + 1) script += self.get_indent_string(indent) + "count += 1\n" script += self.get_indent_string(indent) + "if count >= num_trans and count >= num_sans: break\n" return script - def do_trans_run(self, script, exp, indent, settings): + def do_trans_run(self, script, exp, indent): script += self.do_sample_environment(exp, indent) waitfor = "uamps" rtype = "histogram" fulltitle = f"{{{exp.sample}}}" - if settings.collection_mode == DataCollection.EVENTS: + if self.settings.collection_mode == DataCollection.EVENTS: rtype = DataCollection.EVENTS if exp.trans_wait == "Frames": waitfor = "frames" @@ -127,22 +130,22 @@ def do_trans_run(self, script, exp, indent, settings): elif self.instrument == "LOQ": script += self.get_indent_string(indent) + f"do_trans(title='{fulltitle}', pos='{exp.position}', {waitfor}={exp.trans}, thickness={exp.thickness}, aperture='small')\n" else: - script += self.get_indent_string(indent) + f"do_trans(title='{fulltitle}', pos='{exp.position}', {waitfor}={exp.trans}, thickness={exp.thickness}, dae='transmission', aperture='{self.aperture_string(settings.a2_setting_trans)}')\n" + script += self.get_indent_string(indent) + f"do_trans(title='{fulltitle}', pos='{exp.position}', {waitfor}={exp.trans}, thickness={exp.thickness}, dae='transmission', aperture='{self.aperture_string(self.settings.a2_setting_trans)}')\n" script += self.get_indent_string(indent) + exp.post_command + "\n" return script - def do_sans_run(self, script, exp, indent, settings): + def do_sans_run(self, script, exp, indent): script += self.do_sample_environment(exp, indent) waitfor = "uamps" rtype = "histogram" fulltitle = f"{{{exp.sample}}}" if exp.background: fulltitle += f"_{{{exp.background}}}" - if settings.collection_mode in ["Events", "TRANSSANS_Events"]: + if self.settings.collection_mode in ["Events", "TRANSSANS_Events"]: rtype = "event" - elif settings.collection_mode == "SESANS": + elif self.settings.collection_mode == "SESANS": rtype = "sesans" - if settings.collection_mode in ["TRANSSANS_Events", "TRANSSANS_Histogram"]: + if self.settings.collection_mode in ["TRANSSANS_Events", "TRANSSANS_Histogram"]: fulltitle += "_TRANS" if exp.sans_wait == "Frames": waitfor = "frames" @@ -153,7 +156,7 @@ def do_sans_run(self, script, exp, indent, settings): if self.instrument == "LARMOR" or self.instrument == "ZOOM": script += self.get_indent_string(indent) + f"do_sans(title='{fulltitle}', pos='{exp.position}', {waitfor}={exp.sans}, thickness={exp.thickness}, dae='{rtype}')\n" else: - script += self.get_indent_string(indent) + f"do_sans(title='{fulltitle}', pos='{exp.position}', {waitfor}={exp.sans}, thickness={exp.thickness}, dae='{rtype}', aperture='{self.aperture_string(settings.a2_setting_sans)}')\n" + script += self.get_indent_string(indent) + f"do_sans(title='{fulltitle}', pos='{exp.position}', {waitfor}={exp.sans}, thickness={exp.thickness}, dae='{rtype}', aperture='{self.aperture_string(self.settings.a2_setting_sans)}')\n" script += self.get_indent_string(indent) + exp.post_command + "\n" return script @@ -165,7 +168,7 @@ def aperture_string(self, size): return "medium" elif size in [A2Setting.SMALL, A2Setting.LARMOR_SMALL]: return "small" - elif size in [A2Setting.X_LARGE, A2Setting.Larmor]: + elif size in [A2Setting.X_LARGE]: return "xlarge" return "" @@ -205,12 +208,12 @@ def do_sample_environment(self, exp, indent): script += self.get_indent_string(indent) + f"g.cset(SHEAR_ANGLE_2={exp.shear_angle2})\n" return script - def estimate_run_time(self, runs, settings, estimation): + def estimate_run_time(self, runs, estimation): minutes = 0 - if settings.num_sans > 0: - minutes += self.calculate_time(runs, True, estimation.count_rate, estimation.move_time) * settings.num_sans - if settings.num_trans > 0: - minutes += self.calculate_time(runs, False, estimation.count_rate, estimation.move_time) * settings.num_trans + if self.settings.num_sans > 0: + minutes += self.calculate_time(runs, True, estimation.count_rate, estimation.move_time) * self.settings.num_sans + if self.settings.num_trans > 0: + minutes += self.calculate_time(runs, False, estimation.count_rate, estimation.move_time) * self.settings.num_trans time = datetime(1, 1, 1, 0, 0, 0) + timedelta(minutes=minutes) return time.strftime("%H:%M:%S") diff --git a/SettingsPanel.py b/SettingsPanel.py index 3ed405c..b75eb81 100644 --- a/SettingsPanel.py +++ b/SettingsPanel.py @@ -100,7 +100,7 @@ def create_widgets(self): ttk.Label(grid_frame, text="Sample Geometry:").grid(row=7, column=0, sticky=tk.E,padx=5, pady=5) self.com_sample_geo = ttk.Combobox(grid_frame, values=self.geometry_choices) self.com_sample_geo.grid(row=7, column=1, columnspan=2, padx=5, pady=5) - self.com_sample_geo.set(Geometry.get_choice_string(self.settings.geometry)) # Set default value + self.com_sample_geo.set(Geometry.get_choice_string(self.settings.sample_geometry)) # Set default value self.com_sample_geo.bind("", self.update_sample_geometry) # Sample Height @@ -157,7 +157,7 @@ def update_a2_trans(self, event): self.settings.a2_setting_trans = self.com_a2_trans.get() def update_sample_geometry(self, event): - self.settings.geometry = Geometry.get_ordinal_from_string(self.com_sample_geo.get()) + self.settings.sample_geometry = Geometry.get_ordinal_from_string(self.com_sample_geo.get()) def update_sample_height(self, event): self.settings.sample_height = self.txt_sample_height.get() From 3ee175cb8920380eb10df90cfe4479804f64bc11 Mon Sep 17 00:00:00 2001 From: Dirk Honecker Date: Tue, 7 Jan 2025 14:38:40 +0000 Subject: [PATCH 32/88] Load in data from table into script --- MainWindow.py | 25 +++++++++++++++---------- TableDataConstructor.py | 13 ++++++++----- TableWidget.py | 12 +++--------- 3 files changed, 26 insertions(+), 24 deletions(-) diff --git a/MainWindow.py b/MainWindow.py index 397f66d..ec655ef 100644 --- a/MainWindow.py +++ b/MainWindow.py @@ -146,28 +146,35 @@ def load_last_instrument(self): def pnl_settings_on_write_clicked(self): filetypes = [("genie_python Files", "*.py")] if self.experiment.is_instrument in ["LARMOR", "LOQ", "ZOOM", "SANS2D"] else [("Open Genie Files", "*.gcl")] filepath = filedialog.asksaveasfilename(defaultextension=filetypes[0][1], filetypes=filetypes) - constructor = TableDataConstructor(TableEntries) - self.data = constructor.create_experiments(self.data_frame.data.values()) + constructor = TableDataConstructor(TableEntries) + + + self.data = constructor.construct_data(self.data_frame.table.model.getAllCells()) + self.experiment_data=constructor.create_experiments(self.data.values()) + if filepath: scriptname = filepath.split("/")[-1].replace(" ", "_").rsplit(".", 1)[0] script_generator= ScriptGenerator(self.settings) - script = script_generator.generate_script(self.data, scriptname) + script = script_generator.generate_script(self.experiment_data, scriptname) with open(filepath, "w") as file: file.write(script) def pnl_settings_on_preview_clicked(self): self.script_generator= ScriptGenerator(self.settings) - constructor = TableDataConstructor(TableEntries) - self.data = constructor.create_experiments(self.data_frame.data.values()) - self.script = self.script_generator.generate_script(self.data, "my_script") + constructor = TableDataConstructor(TableEntries) + + self.data = constructor.construct_data(self.data_frame.table.model.getAllCells()) + self.experiment_data=constructor.create_experiments(self.data.values()) + self.script = self.script_generator.generate_script(self.experiment_data, "my_script") print(self.script) preview_dialog = PreviewDialog(self, self.script) preview_dialog.show() def pnl_estimate_on_calculate_clicked(self): constructor = TableDataConstructor(TableEntries) - self.data = constructor.create_experiments(self.data_frame.data.values()) - self.estimation.script_time = self.script_generator.estimate_run_time(self.data, self.estimation) + self.data = constructor.construct_data(self.data_frame.table.model.getAllCells()) + self.experiment_data=constructor.create_experiments(self.data.values()) + self.estimation.script_time = self.script_generator.estimate_run_time(self.experiment_data, self.estimation) def btn_create_new_table_click(self): self.data =None @@ -179,8 +186,6 @@ def mnu_exit_click(self): def mnu_save_csv_click(self): - constructor = TableDataConstructor(TableEntries) - self.data = constructor.create_experiments(self.data_frame.data.values()) try: self.data_frame.table.exportTable() diff --git a/TableDataConstructor.py b/TableDataConstructor.py index db93395..4ff5b58 100644 --- a/TableDataConstructor.py +++ b/TableDataConstructor.py @@ -7,19 +7,19 @@ def __init__(self, table_entries): def construct_empty_data(self): empty_data = { - 0: {entry: "" for entry in self.table_entries} + 1: {entry: "" for entry in self.table_entries} } return empty_data def construct_data(self, data_list): data = {} - for i, row in enumerate(data_list, start=1): + for i, row in enumerate(data_list.values(), start=1): data[i] = {entry: value for entry, value in zip(self.table_entries, row)} return data def deconstruct_data(self, data_dict): data_list = [] - for key in sorted(data_dict.keys(), key=lambda x: int(x[3:])): + for key in sorted(data_dict.keys()): row = [data_dict[key][entry] for entry in self.table_entries] data_list.append(row) return data_list @@ -58,7 +58,7 @@ def create_experiments(self, data_list): constructor = TableDataConstructor(TableEntries) empty_data = constructor.construct_empty_data() print(empty_data) - + ''' # Example usage TableEntries = "col1, col2, label, col3, col4, label2, col5, col6, label3" constructor = TableDataConstructor(TableEntries) @@ -89,8 +89,11 @@ def create_experiments(self, data_list): data = constructor.construct_data(data_list) print("Data:", data) - + ''' # Create experiments from the constructed data + data_list= {0: ['AB', '10', '', '10', '', '', '', '', '1', '', '', '', '', '', '', '', '', '', ''], 1: ['CD', '3', '', '2', '', '', '', '', '2', '', '', '', '', '', '', '', '', '', ''], 2: ['DF', '2', '', '4', '', '', '', '', '5', '', '', '', '', '', '', '', '', '', '']} + data = constructor.construct_data(data_list) + print("Data:", data) experiments = constructor.create_experiments(data.values()) for exp in experiments: print(exp.position, exp.trans, exp.sans, exp.temperature1, exp.rb_number) diff --git a/TableWidget.py b/TableWidget.py index 20898ee..784a117 100644 --- a/TableWidget.py +++ b/TableWidget.py @@ -88,10 +88,7 @@ def ImportTableData(table, sep=None): #lift focus from cell and row for clean delete table.clearSelected() model.deleteRows() - table.data = dictdata model.importDict(dictdata) - # Update the data in the TableWidget instance - table.parentframe.data = dictdata #reset focus of selected row table.drawCellEntry(1, table.currentcol) table.setSelectedRow(1) @@ -182,7 +179,6 @@ def new(self, evt=None): EMPTY_DATA = constructor.construct_empty_data() #clear focus of selected row self.clearSelected() - self.data = EMPTY_DATA self.model.deleteRows() self.model.importDict(EMPTY_DATA) @@ -215,9 +211,7 @@ def display_sheet_data(self,name=None): sheet_name = name if sheet_name and self.workbook: sheet = self.workbook[sheet_name] - #TableEntries = "col1, col2, label, col3, col4, label2, col5, col6, label3" - constructor = TableDataConstructor(TableEntries) - self.data = constructor.construct_data(sheet.iter_rows(values_only=True)) + #TableEntries = "col1, col2, label, col3, col4, label2, col5, col6, label3" self.model.importDict(data) self.redraw() @@ -390,8 +384,8 @@ def __init__(self, parent=None, data=None): self.parent = parent if data is None: data = TableWidget.EMPTY_DATA - self.data = data - self.table = CustomTableCanvas(self, data=self.data ) + + self.table = CustomTableCanvas(self, data=data ) #table.importCSV("test.csv") print (self.table.model.columnNames) #table.model.data[1]["a"] = "XX" From 1000c94ca4e71ffa8683c73162d298a735332cf9 Mon Sep 17 00:00:00 2001 From: Dirk Honecker Date: Tue, 7 Jan 2025 16:12:09 +0000 Subject: [PATCH 33/88] Create DropdownBox when SANS/TRANS wait is pressed --- Models/ExperimentSettings.py | 26 +++++------ Properties/Settings.py | 26 +++++------ SettingsPanel.py | 39 ++++++++++------ TableWidget.py | 87 +++++++++++++++++++++++++++++++++++- 4 files changed, 137 insertions(+), 41 deletions(-) diff --git a/Models/ExperimentSettings.py b/Models/ExperimentSettings.py index 490a3af..e1b3ace 100644 --- a/Models/ExperimentSettings.py +++ b/Models/ExperimentSettings.py @@ -160,7 +160,7 @@ def run_order(self): @run_order.setter def run_order(self, value): - self._run_order.set(value) + self._run_order.set(str(value)) @property def num_sans(self): @@ -168,7 +168,7 @@ def num_sans(self): @num_sans.setter def num_sans(self, value): - self._num_sans.set(value) + self._num_sans.set(int(value)) @property def num_trans(self): @@ -176,7 +176,7 @@ def num_trans(self): @num_trans.setter def num_trans(self, value): - self._num_trans.set(value) + self._num_trans.set(int(value)) @property def loop_per_run(self): @@ -184,7 +184,7 @@ def loop_per_run(self): @loop_per_run.setter def loop_per_run(self, value): - self._loop_per_run.set(value) + self._loop_per_run.set(str(value)) @property def a2_setting_sans(self): @@ -192,7 +192,7 @@ def a2_setting_sans(self): @a2_setting_sans.setter def a2_setting_sans(self, value): - self._a2_setting_sans.set(value) + self._a2_setting_sans.set(str(value)) @property def a2_setting_trans(self): @@ -200,7 +200,7 @@ def a2_setting_trans(self): @a2_setting_trans.setter def a2_setting_trans(self, value): - self._a2_setting_trans.set(value) + self._a2_setting_trans.set(str(value)) @property def sample_geometry(self): @@ -208,7 +208,7 @@ def sample_geometry(self): @sample_geometry.setter def sample_geometry(self, value): - self._sample_geometry.set(value) + self._sample_geometry.set(str(value)) self.sample_width_enabled = value != "Disc" @property @@ -217,9 +217,9 @@ def sample_height(self): @sample_height.setter def sample_height(self, value): - self._sample_height.set(value) + self._sample_height.set(float(value)) if not self.sample_width_enabled: - self.sample_width = value + self.sample_width = float(value) @property def sample_width(self): @@ -227,7 +227,7 @@ def sample_width(self): @sample_width.setter def sample_width(self, value): - self._sample_width.set(value) + self._sample_width.set(float(value)) @property def sample_width_enabled(self): @@ -235,7 +235,7 @@ def sample_width_enabled(self): @sample_width_enabled.setter def sample_width_enabled(self, value): - self._sample_width_enabled.set(value) + self._sample_width_enabled.set(float(value)) if not value: self.sample_width = self.sample_height @@ -245,7 +245,7 @@ def collection_mode(self): @collection_mode.setter def collection_mode(self, value): - self._collection_mode.set(value) + self._collection_mode.set(str(value)) @property def is_instrument(self): @@ -253,7 +253,7 @@ def is_instrument(self): @is_instrument.setter def is_instrument(self, value): - self.instrument.set(value) + self.instrument.set(str(value)) @property def is_valid(self): diff --git a/Properties/Settings.py b/Properties/Settings.py index 5bda93c..ba06326 100644 --- a/Properties/Settings.py +++ b/Properties/Settings.py @@ -12,7 +12,7 @@ def __init__(self, filename="Settings.ini"): @property def last_instrument(self): - return self.dict_settings.get("DEFAULT","LastInstrument") + return str(self.dict_settings.get("DEFAULT","LastInstrument")) @last_instrument.setter def last_instrument(self, value): @@ -21,7 +21,7 @@ def last_instrument(self, value): @property def sample_height(self): - return self.dict_settings.get("DEFAULT","SampleHeight") + return float(self.dict_settings.get("DEFAULT","SampleHeight")) @sample_height.setter def sample_height(self, value): @@ -30,7 +30,7 @@ def sample_height(self, value): @property def sample_width(self): - return self.dict_settings.get("DEFAULT","SampleWidth") + return float(self.dict_settings.get("DEFAULT","SampleWidth")) @sample_width.setter def sample_width(self, value): @@ -39,7 +39,7 @@ def sample_width(self, value): @property def sample_geometry(self): - return self.dict_settings.get("DEFAULT","Geometry") + return str(self.dict_settings.get("DEFAULT","Geometry")) @sample_geometry.setter def sample_geometry(self, value): @@ -48,7 +48,7 @@ def sample_geometry(self, value): @property def run_order(self): - return self.dict_settings.get("DEFAULT","RunOrder") + return str(self.dict_settings.get("DEFAULT","RunOrder")) @run_order.setter def run_order(self, value): @@ -57,7 +57,7 @@ def run_order(self, value): @property def num_trans(self): - return self.dict_settings.get("DEFAULT","NumTrans") + return int(self.dict_settings.get("DEFAULT","NumTrans")) @num_trans.setter def num_trans(self, value): @@ -66,7 +66,7 @@ def num_trans(self, value): @property def num_sans(self): - return self.dict_settings.get("DEFAULT","NumSans") + return int(self.dict_settings.get("DEFAULT","NumSans")) @num_sans.setter def num_sans(self, value): @@ -75,7 +75,7 @@ def num_sans(self, value): @property def loop_per_run(self): - return self.dict_settings.get("DEFAULT","LoopPerRun") + return str(self.dict_settings.get("DEFAULT","LoopPerRun")) @loop_per_run.setter def loop_per_run(self, value): @@ -84,7 +84,7 @@ def loop_per_run(self, value): @property def a2_setting_sans(self): - return self.dict_settings.get("DEFAULT","A2SettingSans") + return str(self.dict_settings.get("DEFAULT","A2SettingSans")) @a2_setting_sans.setter def a2_setting_sans(self, value): @@ -93,7 +93,7 @@ def a2_setting_sans(self, value): @property def a2_setting_trans(self): - return self.dict_settings.get("DEFAULT","A2SettingTrans") + return str(self.dict_settings.get("DEFAULT","A2SettingTrans")) @a2_setting_trans.setter def a2_setting_trans(self, value): @@ -102,7 +102,7 @@ def a2_setting_trans(self, value): @property def collection_mode(self): - return self.dict_settings.get("DEFAULT","CollectionMode") + return str(self.dict_settings.get("DEFAULT","CollectionMode")) @collection_mode.setter def collection_mode(self, value): @@ -111,7 +111,7 @@ def collection_mode(self, value): @property def count_rate(self): - return self.dict_settings.get("DEFAULT","CountRate") + return float(self.dict_settings.get("DEFAULT","CountRate")) @count_rate.setter def count_rate(self, value): @@ -120,7 +120,7 @@ def count_rate(self, value): @property def move_time(self): - return self.dict_settings.get("DEFAULT","MoveTime") + return float(self.dict_settings.get("DEFAULT","MoveTime")) @move_time.setter def move_time(self, value): diff --git a/SettingsPanel.py b/SettingsPanel.py index b75eb81..d3acda8 100644 --- a/SettingsPanel.py +++ b/SettingsPanel.py @@ -133,39 +133,50 @@ def create_widgets(self): # Define the Settings update methods def update_order(self, event): - self.settings.run_order = RunOrder.get_ordinal_from_string(self.com_order.get()) + self.settings.run_order = str(RunOrder.get_ordinal_from_string(self.com_order.get())) def update_num_sans(self, event): - self.settings.num_sans = self.txt_times_sans.get() + try: + self.settings.num_sans = int(self.txt_times_sans.get()) + except ValueError: + # Handle the error, e.g., show a message box or log the error + print("Invalid input for num_sans. Please enter an integer.") def update_num_trans(self, event): - self.settings.num_trans = self.txt_times_trans.get() + try: + self.settings.num_trans = int(self.txt_times_trans.get()) + except ValueError: + # Handle the error, e.g., show a message box or log the error + print("Invalid input for num_trans. Please enter an integer.") def update_loop_per_run(self, event): self.settings.loop_per_run = self.loop_run_var.get() def update_collection_mode(self, event): - self.settings.collection_mode = self.com_collection_mode.get() - - def update_collection_mode(self, event): - self.settings.collection_mode = self.com_collection_mode.get() + self.settings.collection_mode = str(self.com_collection_mode.get()) def update_a2_sans(self, event): - self.settings.a2_setting_sans = self.com_a2_sans.get() + self.settings.a2_setting_sans = str(self.com_a2_sans.get()) def update_a2_trans(self, event): - self.settings.a2_setting_trans = self.com_a2_trans.get() + self.settings.a2_setting_trans = str(self.com_a2_trans.get()) def update_sample_geometry(self, event): - self.settings.sample_geometry = Geometry.get_ordinal_from_string(self.com_sample_geo.get()) + self.settings.sample_geometry = str(Geometry.get_ordinal_from_string(self.com_sample_geo.get())) def update_sample_height(self, event): - self.settings.sample_height = self.txt_sample_height.get() + try: + self.settings.sample_height = float(self.txt_sample_height.get()) + except ValueError: + # Handle the error, e.g., show a message box or log the error + print("Invalid input for sample_height. Please enter a number.") def update_sample_width(self, event): - self.settings.sample_width = self.txt_sample_width.get() - - + try: + self.settings.sample_width = float(self.txt_sample_width.get()) + except ValueError: + # Handle the error, e.g., show a message box or log the error + print("Invalid input for sample_height. Please enter a number.") def btn_preview_script_click(self): if self.on_preview_clicked: diff --git a/TableWidget.py b/TableWidget.py index 784a117..6106bc7 100644 --- a/TableWidget.py +++ b/TableWidget.py @@ -8,7 +8,11 @@ from TableDataConstructor import TableDataConstructor TableEntries = "POSITION, TRANS, TRANS_WAIT, SANS, SANS_WAIT, PERIOD, SAMPLE_IDENTIFICATION_STRING, BACKGROUND, THICKNESS, TEMPERATURE, TEMPERATURE2, FIELD, SHEAR_RATE_1, SHEAR_RATE_2, SHEAR_ANGLE_1, SHEAR_ANGLE_2, PRE_COMMAND, POST_COMMAND, RB_NUMBER" - +#Get position of entries +entries = TableEntries.split(", ") +TRANS_WAIT_POS = entries.index("TRANS_WAIT") +SANS_WAIT_POS = entries.index("SANS_WAIT") + data=None @@ -289,6 +293,87 @@ def move_to_next_row(self, event): self.drawSelectedRect(self.currentrow, self.currentcol) self.drawSelectedRow() + def drawCellEntry(self, row, col, text=None): + if self.read_only: + return + h = self.rowheight + model = self.getModel() + cellvalue = model.getCellRecord(row, col) + #create dropdown list for Tr + if col == TRANS_WAIT_POS or col == SANS_WAIT_POS: + values = ['Option1', 'Option2', 'Option3'] + x1, y1, x2, y2 = self.getCellCoords(row, col) + w = x2 - x1 + + + def callback(value): + model.setValueAt(value, row, col) + self.drawText(row, col, value, 'black', align=self.align) + self.delete('entry') + + self.cellentry = Toplevel(self.parentframe) + self.cellentry.geometry(f'{w}x{h}+{x1}+{y1}') + self.cellentry.overrideredirect(1) + var = StringVar(value=cellvalue) + option_menu = OptionMenu(self.cellentry, var, *values, command=callback) + option_menu.pack() + self.cellentry.bind('', lambda e: self.cellentry.destroy()) + self.cellentry.focus_set() + else: + # Existing entry handling code + if Formula.isFormula(cellvalue): + return + else: + text = self.model.getValueAt(row, col) + x1,y1,x2,y2 = self.getCellCoords(row,col) + w=x2-x1 + #Draw an entry window + txtvar = StringVar() + txtvar.set(text) + def callback(e): + value = txtvar.get() + if value == '=': + #do a dialog that gets the formula into a text area + #then they can click on the cells they want + #when done the user presses ok and its entered into the cell + self.cellentry.destroy() + #its all done here.. + self.formula_Dialog(row, col) + return + + coltype = self.model.getColumnType(col) + if coltype == 'number': + sta = self.checkDataEntry(e) + if sta == 1: + model.setValueAt(value,row,col) + elif coltype == 'text': + model.setValueAt(value,row,col) + + color = self.model.getColorAt(row,col,'fg') + self.drawText(row, col, value, color, align=self.align) + if e.keysym=='Return': + self.delete('entry') + #self.drawRect(row, col) + #self.gotonextCell(e) + return + + self.cellentry=Entry(self.parentframe,width=20, + textvariable=txtvar, + #bg=self.entrybackgr, + #relief=FLAT, + takefocus=1, + font=self.thefont) + self.cellentry.icursor(END) + self.cellentry.bind('', callback) + self.cellentry.bind('', callback) + self.cellentry.focus_set() + self.entrywin=self.create_window(x1+self.inset,y1+self.inset, + width=w-self.inset*2,height=h-self.inset*2, + window=self.cellentry,anchor='nw', + tag='entry') + + return + def popupMenu(self, event, rows=None, cols=None, outside=None): """Add left and right click behaviour for canvas, should not have to override this function, it will take its values from defined dicts in constructor""" From c757ff44deecd4aa4710bdeba1d3a0d3fcb4dbd4 Mon Sep 17 00:00:00 2001 From: Dirk Honecker Date: Tue, 7 Jan 2025 16:23:07 +0000 Subject: [PATCH 34/88] Add correct time choices --- TableWidget.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/TableWidget.py b/TableWidget.py index 6106bc7..ecbb86b 100644 --- a/TableWidget.py +++ b/TableWidget.py @@ -6,6 +6,7 @@ import random from collections import OrderedDict from TableDataConstructor import TableDataConstructor +from Models.WaitForUnits import WaitForUnits TableEntries = "POSITION, TRANS, TRANS_WAIT, SANS, SANS_WAIT, PERIOD, SAMPLE_IDENTIFICATION_STRING, BACKGROUND, THICKNESS, TEMPERATURE, TEMPERATURE2, FIELD, SHEAR_RATE_1, SHEAR_RATE_2, SHEAR_ANGLE_1, SHEAR_ANGLE_2, PRE_COMMAND, POST_COMMAND, RB_NUMBER" #Get position of entries @@ -301,22 +302,21 @@ def drawCellEntry(self, row, col, text=None): cellvalue = model.getCellRecord(row, col) #create dropdown list for Tr if col == TRANS_WAIT_POS or col == SANS_WAIT_POS: - values = ['Option1', 'Option2', 'Option3'] + values = WaitForUnits.choices x1, y1, x2, y2 = self.getCellCoords(row, col) w = x2 - x1 - + h = y2 - y1 def callback(value): model.setValueAt(value, row, col) self.drawText(row, col, value, 'black', align=self.align) self.delete('entry') - self.cellentry = Toplevel(self.parentframe) - self.cellentry.geometry(f'{w}x{h}+{x1}+{y1}') - self.cellentry.overrideredirect(1) + self.cellentry = Frame(self.parentframe, width=w, height=h) + self.cellentry.place(x=x1, y=y1) var = StringVar(value=cellvalue) option_menu = OptionMenu(self.cellentry, var, *values, command=callback) - option_menu.pack() + option_menu.grid(row=0, column=0, sticky="nsew") self.cellentry.bind('', lambda e: self.cellentry.destroy()) self.cellentry.focus_set() else: From 45576d428967c4a36e00b5b7da6a00d29790f7d1 Mon Sep 17 00:00:00 2001 From: Dirk Honecker Date: Tue, 7 Jan 2025 17:40:03 +0000 Subject: [PATCH 35/88] Display estimated time --- EstimationPanel.py | 10 +- MainWindow.py | 2 + Models/Estimation.py | 4 +- Models/WaitForUnits.py | 2 +- Scripting/ScriptGenerator.py | 278 ++++++++++++++++++++--------------- 5 files changed, 166 insertions(+), 130 deletions(-) diff --git a/EstimationPanel.py b/EstimationPanel.py index 6b02877..fdecaf6 100644 --- a/EstimationPanel.py +++ b/EstimationPanel.py @@ -49,13 +49,15 @@ def create_widgets(self): # Est. script time ttk.Label(grid_frame, text="Est. script time:").grid(row=2, column=0, sticky=tk.E,padx=5, pady=5) script_time_frame = ttk.Frame(grid_frame) - script_time_frame.grid(row=2, column=1, columnspan=2,padx=5, pady=5) - self.txt_estimation = ttk.Entry(script_time_frame, width=15, textvariable=self.estimation.script_time, state="readonly") + script_time_frame.grid(row=2, column=1, columnspan=3,padx=5, pady=5) + self.txt_estimation = ttk.Entry(script_time_frame, width=15) + self.txt_estimation.insert(0, self.estimation.script_time) + self.txt_estimation.config(state="readonly") self.txt_estimation.grid(row=0, column=0,padx=5, pady=5) - + # Calculate button self.btn_calculate = ttk.Button(grid_frame, text="Calculate", command=self.btn_calculate_click) - self.btn_calculate.grid(row=3, column=1, columnspan=2,padx=5, pady=5) + self.btn_calculate.grid(row=2, column=3, padx=5, pady=5) # Define the Settings update methods diff --git a/MainWindow.py b/MainWindow.py index ec655ef..bad688d 100644 --- a/MainWindow.py +++ b/MainWindow.py @@ -171,10 +171,12 @@ def pnl_settings_on_preview_clicked(self): preview_dialog.show() def pnl_estimate_on_calculate_clicked(self): + self.script_generator= ScriptGenerator(self.settings) constructor = TableDataConstructor(TableEntries) self.data = constructor.construct_data(self.data_frame.table.model.getAllCells()) self.experiment_data=constructor.create_experiments(self.data.values()) self.estimation.script_time = self.script_generator.estimate_run_time(self.experiment_data, self.estimation) + self.update_estimation_panel() def btn_create_new_table_click(self): self.data =None diff --git a/Models/Estimation.py b/Models/Estimation.py index 2f99e71..2e36525 100644 --- a/Models/Estimation.py +++ b/Models/Estimation.py @@ -8,7 +8,7 @@ def __init__(self, settings=None): self.settings = settings if settings is not None else Settings() self._count_rate = tk.DoubleVar(value=self.settings.count_rate) self._move_time = tk.DoubleVar(value=self.settings.move_time) - self._script_time = tk.DoubleVar(value=0) + self._script_time = tk.StringVar(value="") self._is_valid = tk.BooleanVar(value=True) @@ -78,8 +78,6 @@ def validate(self, *args): error_message = "Count rate cannot be negative!" elif self.move_time < 0: error_message = "Move time cannot be negative!" - elif self.script_time < 0: - error_message = "Script time cannot be negative!" if error_message: self.is_valid = False else: diff --git a/Models/WaitForUnits.py b/Models/WaitForUnits.py index 0df9622..e43cecf 100644 --- a/Models/WaitForUnits.py +++ b/Models/WaitForUnits.py @@ -1,5 +1,5 @@ class WaitForUnits: - MICROAMPS = "Microamp-hours" + MICROAMPS = "uamps" MINUTES = "Minutes" SECONDS = "Seconds" FRAMES = "Frames" diff --git a/Scripting/ScriptGenerator.py b/Scripting/ScriptGenerator.py index aa69856..ef6238d 100644 --- a/Scripting/ScriptGenerator.py +++ b/Scripting/ScriptGenerator.py @@ -33,34 +33,38 @@ def generate_header(self, scriptname): script = "import sys\n" script += "from genie_python import genie as g, BLOCK_NAMES as b\n" script += "sys.path.append(r\"c:\\instrument\\scripts\")\n" - - if self.instrument == "LARMOR": - script += "from instrument.larmor import * # pylint: disable=wildcard-import, unused-wildcard-import\n" - if self.instrument == "LOQ": - script += "from instrument.loq import * # pylint: disable=wildcard-import, unused-wildcard-import\n" - if self.instrument == "ZOOM": - script += "from instrument.zoom import * # pylint: disable=wildcard-import, unused-wildcard-import\n" - if self.instrument == "SANS2D": - script += "from instrument.sans2d import * # pylint: disable=wildcard-import, unused-wildcard-import\n" - + script += self.import_instrument_module() script += f"\ndef {scriptname}():\n" return script + def import_instrument_module(self): + instrument_modules = { + "LARMOR": "from instrument.larmor import * # pylint: disable=wildcard-import, unused-wildcard-import\n", + "LOQ": "from instrument.loq import * # pylint: disable=wildcard-import, unused-wildcard-import\n", + "ZOOM": "from instrument.zoom import * # pylint: disable=wildcard-import, unused-wildcard-import\n", + "SANS2D": "from instrument.sans2d import * # pylint: disable=wildcard-import, unused-wildcard-import\n" + } + return instrument_modules.get(self.instrument, "") + def generate_setup(self): script = "" - script += self.get_indent_string(1) + f"g.change_sample_par('Width', '{self.settings.sample_width}')\n" - script += self.get_indent_string(1) + f"g.change_sample_par('height', '{self.settings.sample_height}')\n" - script += self.get_indent_string(1) + f"g.change_sample_par('Geometry', '{self.settings.sample_geometry}')\n\n" + script += self.change_sample_parameter('Width', self.settings.sample_width) + script += self.change_sample_parameter('height', self.settings.sample_height) + script += self.change_sample_parameter('Geometry', self.settings.sample_geometry) return script + def change_sample_parameter(self, param, value): + return self.get_indent_string(1) + f"g.change_sample_par('{param}', '{value}')\n" + + def check_positions(self, runs): script = "" - if self.instrument == "LARMOR": - position_list = ",".join([exp.position for exp in runs if exp.position]) - if position_list: - script += self.get_indent_string(1) + f"if check_move_pos(['{position_list}']) != True:\n" - script += self.get_indent_string(2) + "print('Invalid positions - script aborted')\n" - script += self.get_indent_string(2) + "return\n\n" + + position_list = ",".join([exp.position for exp in runs if exp.position]) + if position_list: + script += self.get_indent_string(1) + f"if check_move_pos(['{position_list}']) != True:\n" + script += self.get_indent_string(2) + "print('Invalid positions - script aborted')\n" + script += self.get_indent_string(2) + "return\n\n" return script def generate_runs(self, runs): @@ -69,131 +73,144 @@ def generate_runs(self, runs): return script if float(self.settings.run_order) == RunOrder.ALL_TRANS[0]: + script += self.generate_all_trans_runs(runs) + elif float(self.settings.run_order) == RunOrder.ALL_SANS[0]: + script += self.generate_all_sans_runs(runs) + else: + script += self.generate_mixed_runs(runs) + return script + + def generate_all_trans_runs(self, runs): + script = self.get_indent_string(1) + f"for i in range({self.settings.num_trans}):\n" + for exp in runs: + if exp.trans: + script = self.do_trans_run(script, exp, 2) + script += self.get_indent_string(1) + f"for i in range({self.settings.num_sans}):\n" + for exp in runs: + if exp.sans: + script = self.do_sans_run(script, exp, 2) + return script + + def generate_all_sans_runs(self, runs): + script = self.get_indent_string(1) + f"for i in range({self.settings.num_sans}):\n" + for exp in runs: + if exp.sans: + script = self.do_sans_run(script, exp, 2) + if self.settings.num_trans and any(exp.trans for exp in runs): script += self.get_indent_string(1) + f"for i in range({self.settings.num_trans}):\n" for exp in runs: if exp.trans: script = self.do_trans_run(script, exp, 2) - script += self.get_indent_string(1) + f"for i in range({self.settings.num_sans}):\n" - for exp in runs: - if exp.sans: - script = self.do_sans_run(script, exp, 2) - elif float(self.settings.run_order) == RunOrder.ALL_SANS[0]: - script += self.get_indent_string(1) + f"for i in range({self.settings.num_sans}):\n" - for exp in runs: - if exp.sans: - script = self.do_sans_run(script, exp, 2) - if self.settings.num_trans and any(exp.trans for exp in runs): - script += self.get_indent_string(1) + f"for i in range({self.settings.num_trans}):\n" - for exp in runs: - if exp.trans: - script = self.do_trans_run(script, exp, 2) - else: - script += self.get_indent_string(1) + f"num_sans = {self.settings.num_sans}\n" - script += self.get_indent_string(1) + f"num_trans = {self.settings.num_trans}\n" - script += self.get_indent_string(1) + "count = 0\n" - script += self.get_indent_string(1) + "while True:\n" - indent = 2 - for exp in runs: - if float(self.settings.run_order) == RunOrder.TRANS_FIRST[0]: - if exp.trans: - script += self.get_indent_string(indent) + "if count < num_trans:\n" - script = self.do_trans_run(script, exp, indent + 1) - if exp.sans: - script += self.get_indent_string(indent) + "if count < num_sans:\n" - script = self.do_sans_run(script, exp, indent + 1) - else: - if exp.sans: - script += self.get_indent_string(indent) + "if count < num_sans:\n" - script = self.do_sans_run(script, exp, indent + 1) - if exp.trans: - script += self.get_indent_string(indent) + "if count < num_trans:\n" - script = self.do_trans_run(script, exp, indent + 1) - script += self.get_indent_string(indent) + "count += 1\n" - script += self.get_indent_string(indent) + "if count >= num_trans and count >= num_sans: break\n" + return script + + def generate_mixed_runs(self, runs): + script = ( + self.get_indent_string(1) + f"num_sans = {self.settings.num_sans}\n" + + self.get_indent_string(1) + f"num_trans = {self.settings.num_trans}\n" + + self.get_indent_string(1) + "count = 0\n" + + self.get_indent_string(1) + "while True:\n" + ) + indent = 2 + for exp in runs: + if float(self.settings.run_order) == RunOrder.TRANS_FIRST[0]: + script = self.add_trans_first_runs(script, exp, indent) + else: + script = self.add_sans_first_runs(script, exp, indent) + script += self.get_indent_string(indent) + "count += 1\n" + script += self.get_indent_string(indent) + "if count >= num_trans and count >= num_sans: break\n" + return script + + def add_trans_first_runs(self, script, exp, indent): + if exp.trans: + script += self.get_indent_string(indent) + "if count < num_trans:\n" + script = self.do_trans_run(script, exp, indent + 1) + if exp.sans: + script += self.get_indent_string(indent) + "if count < num_sans:\n" + script = self.do_sans_run(script, exp, indent + 1) + return script + + def add_sans_first_runs(self, script, exp, indent): + if exp.sans: + script += self.get_indent_string(indent) + "if count < num_sans:\n" + script = self.do_sans_run(script, exp, indent + 1) + if exp.trans: + script += self.get_indent_string(indent) + "if count < num_trans:\n" + script = self.do_trans_run(script, exp, indent + 1) return script def do_trans_run(self, script, exp, indent): script += self.do_sample_environment(exp, indent) - waitfor = "uamps" - rtype = "histogram" - fulltitle = f"{{{exp.sample}}}" - if self.settings.collection_mode == DataCollection.EVENTS: - rtype = DataCollection.EVENTS - if exp.trans_wait == "Frames": - waitfor = "frames" - elif exp.trans_wait == "Seconds": - waitfor = "seconds" - elif exp.trans_wait == "Minutes": - waitfor = "minutes" - if self.instrument == "LARMOR" or self.instrument == "ZOOM": - script += self.get_indent_string(indent) + f"do_trans(title='{fulltitle}', pos='{exp.position}', {waitfor}={exp.trans}, thickness={exp.thickness})\n" - elif self.instrument == "LOQ": - script += self.get_indent_string(indent) + f"do_trans(title='{fulltitle}', pos='{exp.position}', {waitfor}={exp.trans}, thickness={exp.thickness}, aperture='small')\n" - else: - script += self.get_indent_string(indent) + f"do_trans(title='{fulltitle}', pos='{exp.position}', {waitfor}={exp.trans}, thickness={exp.thickness}, dae='transmission', aperture='{self.aperture_string(self.settings.a2_setting_trans)}')\n" + waitfor, rtype, fulltitle = self.get_run_parameters(exp, "trans") + script += self.get_trans_run_command(exp, indent, waitfor, fulltitle) script += self.get_indent_string(indent) + exp.post_command + "\n" return script def do_sans_run(self, script, exp, indent): script += self.do_sample_environment(exp, indent) + waitfor, rtype, fulltitle = self.get_run_parameters(exp, "sans") + script += self.get_sans_run_command(exp, indent, waitfor, fulltitle) + script += self.get_indent_string(indent) + exp.post_command + "\n" + return script + + def get_run_parameters(self, exp, run_type): waitfor = "uamps" rtype = "histogram" fulltitle = f"{{{exp.sample}}}" - if exp.background: - fulltitle += f"_{{{exp.background}}}" - if self.settings.collection_mode in ["Events", "TRANSSANS_Events"]: - rtype = "event" - elif self.settings.collection_mode == "SESANS": - rtype = "sesans" - if self.settings.collection_mode in ["TRANSSANS_Events", "TRANSSANS_Histogram"]: - fulltitle += "_TRANS" - if exp.sans_wait == "Frames": - waitfor = "frames" - elif exp.sans_wait == "Seconds": - waitfor = "seconds" - elif exp.sans_wait == "Minutes": - waitfor = "minutes" - if self.instrument == "LARMOR" or self.instrument == "ZOOM": - script += self.get_indent_string(indent) + f"do_sans(title='{fulltitle}', pos='{exp.position}', {waitfor}={exp.sans}, thickness={exp.thickness}, dae='{rtype}')\n" + if run_type == "trans": + if exp.trans_wait == "Frames": + waitfor = "frames" + elif exp.trans_wait == "Seconds": + waitfor = "seconds" + elif exp.trans_wait == "Minutes": + waitfor = "minutes" else: - script += self.get_indent_string(indent) + f"do_sans(title='{fulltitle}', pos='{exp.position}', {waitfor}={exp.sans}, thickness={exp.thickness}, dae='{rtype}', aperture='{self.aperture_string(self.settings.a2_setting_sans)}')\n" - script += self.get_indent_string(indent) + exp.post_command + "\n" - return script + if exp.background: + fulltitle += f"_{{{exp.background}}}" + if self.settings.collection_mode in ["Events", "TRANSSANS_Events"]: + rtype = "event" + elif self.settings.collection_mode == "SESANS": + rtype = "sesans" + if self.settings.collection_mode in ["TRANSSANS_Events", "TRANSSANS_Histogram"]: + fulltitle += "_TRANS" + if exp.sans_wait == "Frames": + waitfor = "frames" + elif exp.sans_wait == "Seconds": + waitfor = "seconds" + elif exp.sans_wait == "Minutes": + waitfor = "minutes" + return waitfor, rtype, fulltitle + + def get_trans_run_command(self, exp, indent, waitfor, fulltitle): + if self.instrument in ["LARMOR", "ZOOM"]: + return self.get_indent_string(indent) + f"do_trans(title='{fulltitle}', pos='{exp.position}', {waitfor}={exp.trans}, thickness={exp.thickness})\n" + elif self.instrument == "LOQ": + return self.get_indent_string(indent) + f"do_trans(title='{fulltitle}', pos='{exp.position}', {waitfor}={exp.trans}, thickness={exp.thickness}, aperture='small')\n" + else: + return self.get_indent_string(indent) + f"do_trans(title='{fulltitle}', pos='{exp.position}', {waitfor}={exp.trans}, thickness={exp.thickness}, dae='transmission', aperture='{self.aperture_string(self.settings.a2_setting_trans)}')\n" + + def get_sans_run_command(self, exp, indent, waitfor, fulltitle): + if self.instrument in ["LARMOR", "ZOOM"]: + return self.get_indent_string(indent) + f"do_sans(title='{fulltitle}', pos='{exp.position}', {waitfor}={exp.sans}, thickness={exp.thickness}, dae='histogram')\n" + else: + return self.get_indent_string(indent) + f"do_sans(title='{fulltitle}', pos='{exp.position}', {waitfor}={exp.sans}, thickness={exp.thickness}, dae='histogram', aperture='{self.aperture_string(self.settings.a2_setting_sans)}')\n" + def aperture_string(self, size): - - if size in [A2Setting.LARGE, A2Setting.LARMOR_LARGE]: - return "large" - elif size in [A2Setting.MEDIUM, A2Setting.LARMOR_MEDIUM]: - return "medium" - elif size in [A2Setting.SMALL, A2Setting.LARMOR_SMALL]: - return "small" - elif size in [A2Setting.X_LARGE]: - return "xlarge" - return "" + aperture_sizes = { + A2Setting.LARGE: "large", + A2Setting.LARMOR_LARGE: "large", + A2Setting.MEDIUM: "medium", + A2Setting.LARMOR_MEDIUM: "medium", + A2Setting.SMALL: "small", + A2Setting.LARMOR_SMALL: "small", + A2Setting.X_LARGE: "xlarge" + } + return aperture_sizes.get(size, "") def do_sample_environment(self, exp, indent): script = "" - if exp.temperature1: - temp1 = float(exp.temperature1) - temp1low = temp1 - 1.0 - temp1high = temp1 + 1.0 - if self.is_loq: - script += self.get_indent_string(indent) + f"g.cset(internal_setpoint_1={exp.temperature1})\n" - script += self.get_indent_string(indent) + f"g.cset(\"Julabo_1_ext\", lowlimit={temp1low}, highlimit={temp1high}, runcontrol=True)\n" - else: - script += self.get_indent_string(indent) + f"g.cset(JULABO1_SP={exp.temperature1})\n" - script += self.get_indent_string(indent) + f"g.cset(\"JULABO1_TEMP\", lowlimit={temp1low}, highlimit={temp1high}, runcontrol=True)\n" - if exp.temperature2: - temp2 = float(exp.temperature2) - temp2low = temp2 - 1.0 - temp2high = temp2 + 1.0 - if self.is_loq: - script += self.get_indent_string(indent) + f"g.cset(internal_setpoint_2={exp.temperature2})\n" - script += self.get_indent_string(indent) + f"g.cset(\"Julabo_2_ext\", lowlimit={temp2low}, highlimit={temp2high}, runcontrol=True)\n" - else: - script += self.get_indent_string(indent) + f"g.cset(JULABO2_SP={exp.temperature2})\n" - script += self.get_indent_string(indent) + f"g.cset(\"JULABO2_TEMP\", lowlimit={temp2low}, highlimit={temp2high}, runcontrol=True)\n" + script += self.set_temperature(exp.temperature1, "JULABO1", indent) + script += self.set_temperature(exp.temperature2, "JULABO2", indent) if exp.pre_command: script += self.get_indent_string(indent) + exp.pre_command + "\n" if exp.field: @@ -208,6 +225,23 @@ def do_sample_environment(self, exp, indent): script += self.get_indent_string(indent) + f"g.cset(SHEAR_ANGLE_2={exp.shear_angle2})\n" return script + def set_temperature(self, temperature, julabo, indent): + if temperature: + temp = float(temperature) + temp_low = temp - 1.0 + temp_high = temp + 1.0 + if self.is_loq: + return ( + self.get_indent_string(indent) + f"g.cset(internal_setpoint_{julabo[-1]}={temperature})\n" + + self.get_indent_string(indent) + f"g.cset(\"Julabo_{julabo[-1]}_ext\", lowlimit={temp_low}, highlimit={temp_high}, runcontrol=True)\n" + ) + else: + return ( + self.get_indent_string(indent) + f"g.cset({julabo}_SP={temperature})\n" + + self.get_indent_string(indent) + f"g.cset(\"{julabo}_TEMP\", lowlimit={temp_low}, highlimit={temp_high}, runcontrol=True)\n" + ) + return "" + def estimate_run_time(self, runs, estimation): minutes = 0 if self.settings.num_sans > 0: From 36a727c3dd1ed71022f9829dd14d5d5e9dd6ce15 Mon Sep 17 00:00:00 2001 From: Dirk Honecker Date: Wed, 8 Jan 2025 18:13:41 +0000 Subject: [PATCH 36/88] Create dropdown list for background selection --- TableWidget.py | 37 ++++++++++++++++++++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-) diff --git a/TableWidget.py b/TableWidget.py index ecbb86b..ae484b3 100644 --- a/TableWidget.py +++ b/TableWidget.py @@ -13,7 +13,15 @@ entries = TableEntries.split(", ") TRANS_WAIT_POS = entries.index("TRANS_WAIT") SANS_WAIT_POS = entries.index("SANS_WAIT") - + +BACK_POS = entries.index("BACKGROUND") +SAMPLE_POS = entries.index("SAMPLE_IDENTIFICATION_STRING") + +''' +Check if this allows to Identify the column indices for "back" and "sample" +back_col = self.model.columnNames.index("back") +sample_col = self.model.columnNames.index("sample") +''' data=None @@ -307,6 +315,33 @@ def drawCellEntry(self, row, col, text=None): w = x2 - x1 h = y2 - y1 + def callback(value): + model.setValueAt(value, row, col) + self.drawText(row, col, value, 'black', align=self.align) + self.delete('entry') + + self.cellentry = Frame(self.parentframe, width=w, height=h) + self.cellentry.place(x=x1, y=y1) + var = StringVar(value=cellvalue) + option_menu = OptionMenu(self.cellentry, var, *values, command=callback) + option_menu.grid(row=0, column=0, sticky="nsew") + self.cellentry.bind('', lambda e: self.cellentry.destroy()) + self.cellentry.focus_set() + elif col == BACK_POS: + # Collect unique, non-empty strings from the "sample" column + values = set() + for r in range(self.model.getRowCount()): + value = self.model.getValueAt(r, SAMPLE_POS) + if value: + values.add(value) + values = list(values) + if not values: + return + + x1, y1, x2, y2 = self.getCellCoords(row, col) + w = x2 - x1 + h = y2 - y1 + def callback(value): model.setValueAt(value, row, col) self.drawText(row, col, value, 'black', align=self.align) From 6abecfdbc3eccd8c910f81cc9fd1614a2d192012 Mon Sep 17 00:00:00 2001 From: Dirk Honecker Date: Thu, 9 Jan 2025 09:41:40 +0000 Subject: [PATCH 37/88] Create README.md --- README.md | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 0000000..1cdfa0c --- /dev/null +++ b/README.md @@ -0,0 +1,8 @@ +## Usage + +1) Clone the repository: +git clone https://github.com/dehoni/SANS_Script_GUI.git +cd SANS_Script_GUI + +2) Start the script writer: +python app.py From ea0bbe38eee70443b70ce946102754cf24628eb7 Mon Sep 17 00:00:00 2001 From: Dirk Honecker Date: Wed, 15 Jan 2025 13:02:18 +0000 Subject: [PATCH 38/88] Pyinstaller settings, and catching error with new commands popupmenu --- MainWindow.py | 1 - TableWidget.py | 5 ++++- app.spec | 44 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 48 insertions(+), 2 deletions(-) create mode 100644 app.spec diff --git a/MainWindow.py b/MainWindow.py index bad688d..a90c08d 100644 --- a/MainWindow.py +++ b/MainWindow.py @@ -4,7 +4,6 @@ from Models.DataCollection import DataCollection from Models.InstrumentInfo import InstrumentInfo from Models.Estimation import Estimation -from IO.ExcelIO import ExcelIO from IO.CsvExporter import CsvExporter from Dialogs.PreviewDialog import PreviewDialog from SettingsPanel import SettingsPanel diff --git a/TableWidget.py b/TableWidget.py index ae484b3..5dffda5 100644 --- a/TableWidget.py +++ b/TableWidget.py @@ -478,7 +478,10 @@ def add_defaultcommands(): add_defaultcommands() for action in general: - popupmenu.add_command(label=action, command=defaultactions[action]) + if action in defaultactions: + popupmenu.add_command(label=action, command=defaultactions[action]) + else: + print(f"Warning: No default action defined for '{action}'") popupmenu.add_separator() popupmenu.bind("", popupFocusOut) diff --git a/app.spec b/app.spec new file mode 100644 index 0000000..fbada23 --- /dev/null +++ b/app.spec @@ -0,0 +1,44 @@ +# -*- mode: python ; coding: utf-8 -*- + + +a = Analysis( + ['app.py'], + pathex=[], + binaries=[], + datas=[], + hiddenimports=[], + hookspath=[], + hooksconfig={}, + runtime_hooks=[], + excludes=[], + noarchive=False, + optimize=0, +) +pyz = PYZ(a.pure) + +exe = EXE( + pyz, + a.scripts, + [], + exclude_binaries=True, + name='ISIS SANS Script Writer', + debug=True, + bootloader_ignore_signals=False, + strip=False, + upx=True, + console=True, + disable_windowed_traceback=False, + argv_emulation=False, + target_arch=None, + codesign_identity=None, + entitlements_file=None, +) +coll = COLLECT( + exe, + a.binaries, + a.datas, + strip=False, + upx=True, + upx_exclude=[], + name='app', +) From eb0b2b83d4c6d6632e8a9441b951ca6cc7c121d5 Mon Sep 17 00:00:00 2001 From: Dirk Honecker Date: Wed, 15 Jan 2025 13:35:51 +0000 Subject: [PATCH 39/88] Update README.md --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index 1cdfa0c..563b98c 100644 --- a/README.md +++ b/README.md @@ -6,3 +6,8 @@ cd SANS_Script_GUI 2) Start the script writer: python app.py + +3) to build the app: + pyinstaller app.spec + + The executable can be found in the "dist" folder From f3622b2942505963c63eb148a9f04ce71781ce26 Mon Sep 17 00:00:00 2001 From: Dirk Honecker Date: Wed, 15 Jan 2025 15:03:24 +0000 Subject: [PATCH 40/88] Update README.md --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 563b98c..a3e5e5c 100644 --- a/README.md +++ b/README.md @@ -11,3 +11,5 @@ python app.py pyinstaller app.spec The executable can be found in the "dist" folder + add settings.ini to the "_internal" folder + run "SANS script Writer.exe" From a26d9b049e07685b0d8572d285deb9e95f1feb39 Mon Sep 17 00:00:00 2001 From: Dirk Honecker Date: Wed, 15 Jan 2025 15:05:07 +0000 Subject: [PATCH 41/88] Changes for executable and moving settings from relative folder --- .gitignore | 5 ++++- EstimationPanel.py | 2 +- MainWindow.py | 2 +- Models/Estimation.py | 2 +- Models/ExperimentSettings.py | 4 ++-- Models/InstrumentInfo.py | 2 +- Scripting/ScriptGenerator.py | 2 +- Properties/Settings.ini => Settings.ini | 12 ++++++------ Properties/Settings.py => Settings.py | 0 SettingsPanel.py | 2 +- app.spec | 7 ++++--- 11 files changed, 22 insertions(+), 18 deletions(-) rename Properties/Settings.ini => Settings.ini (75%) rename Properties/Settings.py => Settings.py (100%) diff --git a/.gitignore b/.gitignore index 41e084b..fe96410 100644 --- a/.gitignore +++ b/.gitignore @@ -360,4 +360,7 @@ MigrationBackup/ .ionide/ # Fody - auto-generated XML schema -FodyWeavers.xsd \ No newline at end of file +FodyWeavers.xsd +/build/app +/dist/app +/executable diff --git a/EstimationPanel.py b/EstimationPanel.py index fdecaf6..07c6d24 100644 --- a/EstimationPanel.py +++ b/EstimationPanel.py @@ -1,7 +1,7 @@ import tkinter as tk from tkinter import ttk from Models.Estimation import Estimation -from Properties.Settings import Settings +from Settings import Settings class EstimationPanel(ttk.Frame): def __init__(self, parent, estimation = None, settings = None): diff --git a/MainWindow.py b/MainWindow.py index a90c08d..98a5f15 100644 --- a/MainWindow.py +++ b/MainWindow.py @@ -9,7 +9,7 @@ from SettingsPanel import SettingsPanel from EstimationPanel import EstimationPanel from TableWidget import TableWidget -from Properties.Settings import Settings +from Settings import Settings from Scripting.ScriptGenerator import ScriptGenerator from Models.ExperimentSettings import ExperimentSettings from TableDataConstructor import TableDataConstructor diff --git a/Models/Estimation.py b/Models/Estimation.py index 2e36525..40b571e 100644 --- a/Models/Estimation.py +++ b/Models/Estimation.py @@ -1,5 +1,5 @@ import tkinter as tk -from Properties.Settings import Settings +from Settings import Settings diff --git a/Models/ExperimentSettings.py b/Models/ExperimentSettings.py index e1b3ace..aab40ae 100644 --- a/Models/ExperimentSettings.py +++ b/Models/ExperimentSettings.py @@ -1,9 +1,9 @@ import tkinter as tk -from Properties.Settings import Settings +from Settings import Settings from Models.A2Setting import A2Setting from Models.DataCollection import DataCollection from Models.Estimation import Estimation -from Properties.Settings import Settings +from Settings import Settings from Models.RunOrder import RunOrder from Models.Geometry import Geometry diff --git a/Models/InstrumentInfo.py b/Models/InstrumentInfo.py index 6ad59ee..c3f3942 100644 --- a/Models/InstrumentInfo.py +++ b/Models/InstrumentInfo.py @@ -1,7 +1,7 @@ import tkinter as tk from tkinter import StringVar, BooleanVar from typing import List -#from Properties.Settings import Settings +#from Settings import Settings from Models.ExperimentSettings import ExperimentSettings class InstrumentInfo: diff --git a/Scripting/ScriptGenerator.py b/Scripting/ScriptGenerator.py index ef6238d..02e998e 100644 --- a/Scripting/ScriptGenerator.py +++ b/Scripting/ScriptGenerator.py @@ -3,7 +3,7 @@ from datetime import datetime, timedelta from Models.Estimation import Estimation from Models.Experiment import Experiment -from Properties.Settings import Settings +from Settings import Settings from Models.ExperimentSettings import ExperimentSettings from Models.A2Setting import A2Setting from Models.RunOrder import RunOrder diff --git a/Properties/Settings.ini b/Settings.ini similarity index 75% rename from Properties/Settings.ini rename to Settings.ini index 8c9537d..a6c0b47 100644 --- a/Properties/Settings.ini +++ b/Settings.ini @@ -1,14 +1,14 @@ [DEFAULT] lastinstrument = LOQ -sampleheight = 12 -samplewidth = 12 +sampleheight = 10 +samplewidth = 2 geometry = 4 -runorder = 3 -numtrans = 5 +runorder = 2 +numtrans = 3 numsans = 1 loopperrun = True -a2settingsans = Large = 17 mm -a2settingtrans = Large = 17 mm +a2settingsans = Large (17 mm) +a2settingtrans = Large (17 mm) collectionmode = Events tableentries = "POSITION, TRANS, TRANS_WAIT, SANS, SANS_WAIT, PERIOD, SAMPLE_IDENTIFICATION_STRING, BACKGROUND, THICKNESS, TEMPERATURE, TEMPERATURE2, FIELD, SHEAR_RATE_1, SHEAR_RATE_2, SHEAR_ANGLE_1, SHEAR_ANGLE_2, PRE_COMMAND, POST_COMMAND, RB_NUMBER" countrate = 170 diff --git a/Properties/Settings.py b/Settings.py similarity index 100% rename from Properties/Settings.py rename to Settings.py diff --git a/SettingsPanel.py b/SettingsPanel.py index d3acda8..4368376 100644 --- a/SettingsPanel.py +++ b/SettingsPanel.py @@ -1,6 +1,6 @@ import tkinter as tk from tkinter import ttk -from Properties.Settings import Settings +from Settings import Settings from Models.RunOrder import RunOrder from Models.Geometry import Geometry from Models.ExperimentSettings import ExperimentSettings diff --git a/app.spec b/app.spec index fbada23..3c5a556 100644 --- a/app.spec +++ b/app.spec @@ -21,12 +21,13 @@ exe = EXE( a.scripts, [], exclude_binaries=True, - name='ISIS SANS Script Writer', - debug=True, + name='SANS_ScriptWriter', + debug=False, bootloader_ignore_signals=False, strip=False, upx=True, - console=True, + console=False, + icon='SANS_Icon.ico', disable_windowed_traceback=False, argv_emulation=False, target_arch=None, From 9225bc49f2d751f23589e4365656ccd4138b5b59 Mon Sep 17 00:00:00 2001 From: Dirk Honecker Date: Tue, 13 May 2025 10:40:24 +0100 Subject: [PATCH 42/88] Update README.md --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index a3e5e5c..4299861 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,5 @@ +Needs the Python packages Tkintertable, openpyxl + ## Usage 1) Clone the repository: From 30cc5d0021866f7a143a2e4b690db8704ec9c1bb Mon Sep 17 00:00:00 2001 From: Dirk Honecker Date: Thu, 13 Aug 2026 13:50:12 +0100 Subject: [PATCH 43/88] clean up test file --- TextFile2.py | 7 ++----- Transcript_GitCopilot.txt | 3 +++ 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/TextFile2.py b/TextFile2.py index 100c904..1265f3a 100644 --- a/TextFile2.py +++ b/TextFile2.py @@ -3,9 +3,6 @@ from tkinter.scrolledtext import ScrolledText import pandas as pd from Models.DataCollection import DataCollection -from ViewModels.DataGridVM import DataGridVM -#from ViewModels.SettingsPanelVM import SettingsPanelVM -#from ViewModels.InstrumentVM import InstrumentVM from Models.InstrumentInfo import InstrumentInfo from Models.Estimation import Estimation from Models.ExperimentSettings import ExperimentSettings @@ -15,7 +12,7 @@ from SettingsPanel import SettingsPanel from EstimationPanel import EstimationPanel from TableWidget import TableWidget -from Properties.Settings import Settings +from Settings import Settings from Scripting.ScriptGenerator import ScriptGenerator from datetime import datetime, timedelta @@ -30,7 +27,7 @@ settings = ExperimentSettings() settings(sample_width=10, sample_height=10, sample_geometry="Flat ", order="AllTrans", num_trans=1, num_sans=1, collection_mode="Events", a2_setting_trans="Large", a2_setting_sans="Large") - estimation = Estimation(count_rate=100, move_time=10) + estimation = Estimation(settings=settings) instrument = "LARMOR" generator = ScriptGenerator(instrument) script = generator.generate_script(runs, settings, "my_script") diff --git a/Transcript_GitCopilot.txt b/Transcript_GitCopilot.txt index f0585ac..76aa41c 100644 --- a/Transcript_GitCopilot.txt +++ b/Transcript_GitCopilot.txt @@ -1,3 +1,6 @@ +to create exe +https://github.com/oop7/Py-to-EXE-Guideau + dehoni: @workspace /explain Workspace: The provided code is a C# file named From 6072488872bbee6bbf8a030afe5f0f64381ca6a8 Mon Sep 17 00:00:00 2001 From: Dirk Honecker Date: Thu, 13 Aug 2026 13:58:40 +0100 Subject: [PATCH 44/88] create folder Python_SANS_Script_GUI --- App.py => Python_SANS_Script_GUI/App.py | 0 .../Dialogs}/PreviewDialog.py | 0 .../EstimationPanel.py | 0 .../IO}/CsvExporter.py | 0 {IO => Python_SANS_Script_GUI/IO}/ExcelIO.py | 0 .../MainWindow.py | 0 .../Models}/A2Setting.py | 0 .../Models}/DataCollection.py | 0 .../Models}/Estimation.py | 0 .../Models}/Experiment.py | 0 .../Models}/ExperimentSettings.py | 0 .../Models}/Geometry.py | 0 .../Models}/InstrumentInfo.py | 0 .../Models}/RunOrder.py | 0 .../Models}/WaitForUnits.py | 0 .../Properties}/AssemblyInfo.py | 0 .../SANS_Icon.ico | Bin .../Scripting}/ScriptGenerator.py | 0 .../Settings.ini | 0 .../Settings.py | 0 .../SettingsPanel.py | 0 .../TableDataConstructor.py | 0 .../TableTest.py | 0 .../TableWidget.py | 0 .../TextFile2.py | 0 app.spec => Python_SANS_Script_GUI/app.spec | 0 Transcript_GitCopilot.txt | 3940 ----------------- 27 files changed, 3940 deletions(-) rename App.py => Python_SANS_Script_GUI/App.py (100%) rename {Dialogs => Python_SANS_Script_GUI/Dialogs}/PreviewDialog.py (100%) rename EstimationPanel.py => Python_SANS_Script_GUI/EstimationPanel.py (100%) rename {IO => Python_SANS_Script_GUI/IO}/CsvExporter.py (100%) rename {IO => Python_SANS_Script_GUI/IO}/ExcelIO.py (100%) rename MainWindow.py => Python_SANS_Script_GUI/MainWindow.py (100%) rename {Models => Python_SANS_Script_GUI/Models}/A2Setting.py (100%) rename {Models => Python_SANS_Script_GUI/Models}/DataCollection.py (100%) rename {Models => Python_SANS_Script_GUI/Models}/Estimation.py (100%) rename {Models => Python_SANS_Script_GUI/Models}/Experiment.py (100%) rename {Models => Python_SANS_Script_GUI/Models}/ExperimentSettings.py (100%) rename {Models => Python_SANS_Script_GUI/Models}/Geometry.py (100%) rename {Models => Python_SANS_Script_GUI/Models}/InstrumentInfo.py (100%) rename {Models => Python_SANS_Script_GUI/Models}/RunOrder.py (100%) rename {Models => Python_SANS_Script_GUI/Models}/WaitForUnits.py (100%) rename {Properties => Python_SANS_Script_GUI/Properties}/AssemblyInfo.py (100%) rename SANS_Icon.ico => Python_SANS_Script_GUI/SANS_Icon.ico (100%) rename {Scripting => Python_SANS_Script_GUI/Scripting}/ScriptGenerator.py (100%) rename Settings.ini => Python_SANS_Script_GUI/Settings.ini (100%) rename Settings.py => Python_SANS_Script_GUI/Settings.py (100%) rename SettingsPanel.py => Python_SANS_Script_GUI/SettingsPanel.py (100%) rename TableDataConstructor.py => Python_SANS_Script_GUI/TableDataConstructor.py (100%) rename TableTest.py => Python_SANS_Script_GUI/TableTest.py (100%) rename TableWidget.py => Python_SANS_Script_GUI/TableWidget.py (100%) rename TextFile2.py => Python_SANS_Script_GUI/TextFile2.py (100%) rename app.spec => Python_SANS_Script_GUI/app.spec (100%) delete mode 100644 Transcript_GitCopilot.txt diff --git a/App.py b/Python_SANS_Script_GUI/App.py similarity index 100% rename from App.py rename to Python_SANS_Script_GUI/App.py diff --git a/Dialogs/PreviewDialog.py b/Python_SANS_Script_GUI/Dialogs/PreviewDialog.py similarity index 100% rename from Dialogs/PreviewDialog.py rename to Python_SANS_Script_GUI/Dialogs/PreviewDialog.py diff --git a/EstimationPanel.py b/Python_SANS_Script_GUI/EstimationPanel.py similarity index 100% rename from EstimationPanel.py rename to Python_SANS_Script_GUI/EstimationPanel.py diff --git a/IO/CsvExporter.py b/Python_SANS_Script_GUI/IO/CsvExporter.py similarity index 100% rename from IO/CsvExporter.py rename to Python_SANS_Script_GUI/IO/CsvExporter.py diff --git a/IO/ExcelIO.py b/Python_SANS_Script_GUI/IO/ExcelIO.py similarity index 100% rename from IO/ExcelIO.py rename to Python_SANS_Script_GUI/IO/ExcelIO.py diff --git a/MainWindow.py b/Python_SANS_Script_GUI/MainWindow.py similarity index 100% rename from MainWindow.py rename to Python_SANS_Script_GUI/MainWindow.py diff --git a/Models/A2Setting.py b/Python_SANS_Script_GUI/Models/A2Setting.py similarity index 100% rename from Models/A2Setting.py rename to Python_SANS_Script_GUI/Models/A2Setting.py diff --git a/Models/DataCollection.py b/Python_SANS_Script_GUI/Models/DataCollection.py similarity index 100% rename from Models/DataCollection.py rename to Python_SANS_Script_GUI/Models/DataCollection.py diff --git a/Models/Estimation.py b/Python_SANS_Script_GUI/Models/Estimation.py similarity index 100% rename from Models/Estimation.py rename to Python_SANS_Script_GUI/Models/Estimation.py diff --git a/Models/Experiment.py b/Python_SANS_Script_GUI/Models/Experiment.py similarity index 100% rename from Models/Experiment.py rename to Python_SANS_Script_GUI/Models/Experiment.py diff --git a/Models/ExperimentSettings.py b/Python_SANS_Script_GUI/Models/ExperimentSettings.py similarity index 100% rename from Models/ExperimentSettings.py rename to Python_SANS_Script_GUI/Models/ExperimentSettings.py diff --git a/Models/Geometry.py b/Python_SANS_Script_GUI/Models/Geometry.py similarity index 100% rename from Models/Geometry.py rename to Python_SANS_Script_GUI/Models/Geometry.py diff --git a/Models/InstrumentInfo.py b/Python_SANS_Script_GUI/Models/InstrumentInfo.py similarity index 100% rename from Models/InstrumentInfo.py rename to Python_SANS_Script_GUI/Models/InstrumentInfo.py diff --git a/Models/RunOrder.py b/Python_SANS_Script_GUI/Models/RunOrder.py similarity index 100% rename from Models/RunOrder.py rename to Python_SANS_Script_GUI/Models/RunOrder.py diff --git a/Models/WaitForUnits.py b/Python_SANS_Script_GUI/Models/WaitForUnits.py similarity index 100% rename from Models/WaitForUnits.py rename to Python_SANS_Script_GUI/Models/WaitForUnits.py diff --git a/Properties/AssemblyInfo.py b/Python_SANS_Script_GUI/Properties/AssemblyInfo.py similarity index 100% rename from Properties/AssemblyInfo.py rename to Python_SANS_Script_GUI/Properties/AssemblyInfo.py diff --git a/SANS_Icon.ico b/Python_SANS_Script_GUI/SANS_Icon.ico similarity index 100% rename from SANS_Icon.ico rename to Python_SANS_Script_GUI/SANS_Icon.ico diff --git a/Scripting/ScriptGenerator.py b/Python_SANS_Script_GUI/Scripting/ScriptGenerator.py similarity index 100% rename from Scripting/ScriptGenerator.py rename to Python_SANS_Script_GUI/Scripting/ScriptGenerator.py diff --git a/Settings.ini b/Python_SANS_Script_GUI/Settings.ini similarity index 100% rename from Settings.ini rename to Python_SANS_Script_GUI/Settings.ini diff --git a/Settings.py b/Python_SANS_Script_GUI/Settings.py similarity index 100% rename from Settings.py rename to Python_SANS_Script_GUI/Settings.py diff --git a/SettingsPanel.py b/Python_SANS_Script_GUI/SettingsPanel.py similarity index 100% rename from SettingsPanel.py rename to Python_SANS_Script_GUI/SettingsPanel.py diff --git a/TableDataConstructor.py b/Python_SANS_Script_GUI/TableDataConstructor.py similarity index 100% rename from TableDataConstructor.py rename to Python_SANS_Script_GUI/TableDataConstructor.py diff --git a/TableTest.py b/Python_SANS_Script_GUI/TableTest.py similarity index 100% rename from TableTest.py rename to Python_SANS_Script_GUI/TableTest.py diff --git a/TableWidget.py b/Python_SANS_Script_GUI/TableWidget.py similarity index 100% rename from TableWidget.py rename to Python_SANS_Script_GUI/TableWidget.py diff --git a/TextFile2.py b/Python_SANS_Script_GUI/TextFile2.py similarity index 100% rename from TextFile2.py rename to Python_SANS_Script_GUI/TextFile2.py diff --git a/app.spec b/Python_SANS_Script_GUI/app.spec similarity index 100% rename from app.spec rename to Python_SANS_Script_GUI/app.spec diff --git a/Transcript_GitCopilot.txt b/Transcript_GitCopilot.txt deleted file mode 100644 index 76aa41c..0000000 --- a/Transcript_GitCopilot.txt +++ /dev/null @@ -1,3940 +0,0 @@ -to create exe -https://github.com/oop7/Py-to-EXE-Guideau - -dehoni: @workspace /explain - -Workspace: The provided code is a C# file named - -SettingsPanel.xaml.cs - -, which is part of a WPF (Windows Presentation Foundation) application. This file defines the code-behind for a user control named - -SettingsPanel - -. The - -SettingsPanel - - class inherits from - -UserControl - -, which is a base class for defining custom user interface controls in WPF. - -The - -SettingsPanel - - class contains two public events: - -OnPreviewClicked - - and - -OnWriteClicked - -. These events are used to notify subscribers when the corresponding buttons are clicked. Events in C# are a way to provide notifications to other parts of the application when something of interest happens. - -The constructor - -SettingsPanel() - - initializes the component by calling the - -InitializeComponent() - - method. This method is automatically generated and is responsible for loading the XAML-defined UI elements and setting up the control. - -There are two private methods in the class: - -btnPreviewScript_Click - - and - -btnWriteScript_Click - -. These methods are event handlers for button click events. When the - -btnPreviewScript_Click - - method is called (which happens when the associated button is clicked), it checks if there are any subscribers to the - -OnPreviewClicked - - event. If there are, it raises the event by invoking the - -OnPreviewClicked - - delegate, passing the sender and event arguments. - -Similarly, the - -btnWriteScript_Click - - method handles the click event for another button. It checks for subscribers to the - -OnWriteClicked - - event and raises the event if there are any. - -Overall, this code sets up a user control with two buttons, each of which can trigger events that other parts of the application can subscribe to and handle accordingly. This allows for a decoupled design where the logic for handling button clicks can be separated from the user control itself. - -dehoni: Please translate the selected code into python and use approbriate packages. - -GitHub Copilot: Here is the translated code into Python using the `tkinter` package, which is a standard GUI toolkit in Python: - -```python - - -import tkinter as tk -from tkinter import ttk - -class SettingsPanel(ttk.Frame): - def __init__(self, parent): - super().__init__(parent) - self.on_preview_clicked = None - self.on_write_clicked = None - - self.preview_button = ttk.Button(self, text="Preview Script", command=self.btn_preview_script_click) - self.preview_button.pack() - - self.write_button = ttk.Button(self, text="Write Script", command=self.btn_write_script_click) - self.write_button.pack() - - def btn_preview_script_click(self): - if self.on_preview_clicked is not None: - self.on_preview_clicked() - - def btn_write_script_click(self): - if self.on_write_clicked is not None: - self.on_write_clicked() - -if __name__ == "__main__": - root = tk.Tk() - panel = SettingsPanel(root) - panel.pack() - root.mainloop() -``` - -In this Python version: -- The - -SettingsPanel - - class inherits from `ttk.Frame`. -- The `on_preview_clicked` and `on_write_clicked` attributes are used to store callback functions. -- The `btn_preview_script_click` and `btn_write_script_click` methods are defined to call these callbacks if they are set. -- The `ttk.Button` widgets are created for the preview and write actions, and their `command` attributes are set to the corresponding methods. -- The `if __name__ == "__main__":` block creates a simple application to demonstrate the - -SettingsPanel - - usage. - -dehoni: Please translate the selected code into python and use approbriate packages. - -GitHub Copilot: Here is the translated code into Python using the `tkinter` package: - -```python - - -import tkinter as tk -from tkinter import ttk - -class SettingsPanel(ttk.Frame): - def __init__(self, parent): - super().__init__(parent) - - self.settings_vm = SettingsPanelVM() - - self.create_widgets() - - def create_widgets(self): - label = ttk.Label(self, text="Settings", font=("Helvetica", 16, "bold")) - label.pack(pady=10) - - grid_frame = ttk.Frame(self) - grid_frame.pack(padx=10, pady=10) - - # Order - ttk.Label(grid_frame, text="Order:").grid(row=0, column=0, sticky=tk.E) - self.com_order = ttk.Combobox(grid_frame, values=self.settings_vm.order_choices) - self.com_order.grid(row=0, column=1, columnspan=2, padx=5, pady=5) - - # Do SANS - ttk.Label(grid_frame, text="Do SANS:").grid(row=1, column=0, sticky=tk.E) - sans_frame = ttk.Frame(grid_frame) - sans_frame.grid(row=1, column=1, columnspan=2) - self.txt_times_sans = ttk.Entry(sans_frame, width=5) - self.txt_times_sans.pack(side=tk.LEFT, padx=5, pady=5) - ttk.Label(sans_frame, text="time(s)").pack(side=tk.LEFT) - - # Do TRANS - ttk.Label(grid_frame, text="Do TRANS:").grid(row=2, column=0, sticky=tk.E) - trans_frame = ttk.Frame(grid_frame) - trans_frame.grid(row=2, column=1, columnspan=2) - self.txt_times_trans = ttk.Entry(trans_frame, width=5) - self.txt_times_trans.pack(side=tk.LEFT, padx=5, pady=5) - ttk.Label(trans_frame, text="time(s)").pack(side=tk.LEFT) - - # Loop over each run - self.chk_loop_run = ttk.Checkbutton(grid_frame, text="Loop over each run?") - self.chk_loop_run.grid(row=3, column=0, columnspan=3, pady=5) - - # Sample Geometry - ttk.Label(grid_frame, text="Sample Geometry:").grid(row=6, column=0, sticky=tk.E) - self.com_sample_geo = ttk.Combobox(grid_frame, values=self.settings_vm.geometry_choices) - self.com_sample_geo.grid(row=6, column=1, columnspan=2, padx=5, pady=5) - - # Sample Height - ttk.Label(grid_frame, text="Sample Height:").grid(row=7, column=0, sticky=tk.E) - sample_height_frame = ttk.Frame(grid_frame) - sample_height_frame.grid(row=7, column=1, columnspan=2) - self.txt_sample_height = ttk.Entry(sample_height_frame, width=5) - self.txt_sample_height.pack(side=tk.LEFT, padx=5, pady=5) - ttk.Label(sample_height_frame, text="mm").pack(side=tk.LEFT) - - # Sample Width - ttk.Label(grid_frame, text="Sample Width:").grid(row=8, column=0, sticky=tk.E) - sample_width_frame = ttk.Frame(grid_frame) - sample_width_frame.grid(row=8, column=1, columnspan=2) - self.txt_sample_width = ttk.Entry(sample_width_frame, width=5) - self.txt_sample_width.pack(side=tk.LEFT, padx=5, pady=5) - ttk.Label(sample_width_frame, text="mm").pack(side=tk.LEFT) - - # Buttons - button_frame = ttk.Frame(self) - button_frame.pack(pady=10) - self.btn_preview_script = ttk.Button(button_frame, text="Preview Script", command=self.btn_preview_script_click) - self.btn_preview_script.pack(side=tk.LEFT, padx=5) - self.btn_write_script = ttk.Button(button_frame, text="Write Script", command=self.btn_write_script_click) - self.btn_write_script.pack(side=tk.LEFT, padx=5) - - def btn_preview_script_click(self): - if self.on_preview_clicked is not None: - self.on_preview_clicked() - - def btn_write_script_click(self): - if self.on_write_clicked is not None: - self.on_write_clicked() - -class SettingsPanelVM: - def __init__(self): - self.order_choices = ["Choice1", "Choice2", "Choice3"] - self.geometry_choices = ["Geometry1", "Geometry2", "Geometry3"] - -if __name__ == "__main__": - root = tk.Tk() - panel = SettingsPanel(root) - panel.pack() - root.mainloop() -``` - -In this Python version: -- The `SettingsPanel` class inherits from `ttk.Frame`. -- The `SettingsPanelVM` class is a simple ViewModel to provide choices for the comboboxes. -- The `create_widgets` method sets up the UI elements similar to the XAML layout. -- The `btn_preview_script_click` and `btn_write_script_click` methods handle button clicks. -- The `if __name__ == "__main__":` block creates a simple application to demonstrate the `SettingsPanel` usage. - -dehoni: Please translate the selected code into python and use approbriate packages. - -GitHub Copilot: The provided XML file is a project user file for a Visual Studio project. In Python, we can use the `xml.etree.ElementTree` module to create a similar XML structure. Here is the translated code: - -```python - - -import xml.etree.ElementTree as ET - -root = ET.Element("Project", ToolsVersion="14.0", xmlns="http://schemas.microsoft.com/developer/msbuild/2003") - -property_group = ET.SubElement(root, "PropertyGroup") -ET.SubElement(property_group, "PublishUrlHistory").text = "publish\\" -ET.SubElement(property_group, "InstallUrlHistory") -ET.SubElement(property_group, "SupportUrlHistory") -ET.SubElement(property_group, "UpdateUrlHistory") -ET.SubElement(property_group, "BootstrapperUrlHistory") -ET.SubElement(property_group, "ErrorReportUrlHistory") -ET.SubElement(property_group, "FallbackCulture").text = "en-US" -ET.SubElement(property_group, "VerifyUploadedFiles").text = "false" - -tree = ET.ElementTree(root) -tree.write("SANS_Script_GUI_v4.xml", encoding="utf-8", xml_declaration=True) -``` - -This Python script uses the `xml.etree.ElementTree` module to create an XML structure similar to the provided `.csproj.user` file. The resulting XML file will be saved as `SANS_Script_GUI_v4.xml`. - -dehoni: Please translate the selected code into python and use approbriate packages. - -GitHub Copilot: Here is the translated code into Python using the `xml.etree.ElementTree` module: - -```python - - -import xml.etree.ElementTree as ET - -root = ET.Element("Project", ToolsVersion="12.0", DefaultTargets="Build", xmlns="http://schemas.microsoft.com/developer/msbuild/2003") - -ET.SubElement(root, "Import", Project="$(MSBuildExtensionsPath)\\$(MSBuildToolsVersion)\\Microsoft.Common.props", Condition="Exists("$(MSBuildExtensionsPath)\\$(MSBuildToolsVersion)\\Microsoft.Common.props")") - -property_group = ET.SubElement(root, "PropertyGroup") -ET.SubElement(property_group, "Configuration", Condition=" "$(Configuration)" == "" ").text = "Debug" -ET.SubElement(property_group, "Platform", Condition=" "$(Platform)" == "" ").text = "AnyCPU" -ET.SubElement(property_group, "ProjectGuid").text = "{5DBFF2C3-05A0-4B4B-9080-8224D86A7B40}" -ET.SubElement(property_group, "OutputType").text = "WinExe" -ET.SubElement(property_group, "AppDesignerFolder").text = "Properties" -ET.SubElement(property_group, "RootNamespace").text = "LOQ_Script_Gui" -ET.SubElement(property_group, "AssemblyName").text = "SANS_Script_Gui_v4" -ET.SubElement(property_group, "TargetFrameworkVersion").text = "v4.8" -ET.SubElement(property_group, "FileAlignment").text = "512" -ET.SubElement(property_group, "ProjectTypeGuids").text = "{60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}" -ET.SubElement(property_group, "WarningLevel").text = "4" -ET.SubElement(property_group, "IsWebBootstrapper").text = "false" -ET.SubElement(property_group, "TargetFrameworkProfile") -ET.SubElement(property_group, "PublishUrl").text = "publish\\" -ET.SubElement(property_group, "Install").text = "true" -ET.SubElement(property_group, "InstallFrom").text = "Disk" -ET.SubElement(property_group, "UpdateEnabled").text = "false" -ET.SubElement(property_group, "UpdateMode").text = "Foreground" -ET.SubElement(property_group, "UpdateInterval").text = "7" -ET.SubElement(property_group, "UpdateIntervalUnits").text = "Days" -ET.SubElement(property_group, "UpdatePeriodically").text = "false" -ET.SubElement(property_group, "UpdateRequired").text = "false" -ET.SubElement(property_group, "MapFileExtensions").text = "true" -ET.SubElement(property_group, "ApplicationRevision").text = "0" -ET.SubElement(property_group, "ApplicationVersion").text = "1.0.0.%2a" -ET.SubElement(property_group, "UseApplicationTrust").text = "false" -ET.SubElement(property_group, "BootstrapperEnabled").text = "true" - -debug_property_group = ET.SubElement(root, "PropertyGroup", Condition=" "$(Configuration)|$(Platform)" == "Debug|AnyCPU" ") -ET.SubElement(debug_property_group, "PlatformTarget").text = "x64" -ET.SubElement(debug_property_group, "DebugSymbols").text = "true" -ET.SubElement(debug_property_group, "DebugType").text = "full" -ET.SubElement(debug_property_group, "Optimize").text = "false" -ET.SubElement(debug_property_group, "OutputPath").text = "bin\\Debug\\" -ET.SubElement(debug_property_group, "DefineConstants").text = "DEBUG;TRACE" -ET.SubElement(debug_property_group, "ErrorReport").text = "prompt" -ET.SubElement(debug_property_group, "WarningLevel").text = "4" -ET.SubElement(debug_property_group, "Prefer32Bit").text = "false" - -release_property_group = ET.SubElement(root, "PropertyGroup", Condition=" "$(Configuration)|$(Platform)" == "Release|AnyCPU" ") -ET.SubElement(release_property_group, "PlatformTarget").text = "x64" -ET.SubElement(release_property_group, "DebugType").text = "pdbonly" -ET.SubElement(release_property_group, "Optimize").text = "true" -ET.SubElement(release_property_group, "OutputPath").text = "bin\\Release\\" -ET.SubElement(release_property_group, "DefineConstants").text = "TRACE" -ET.SubElement(release_property_group, "ErrorReport").text = "prompt" -ET.SubElement(release_property_group, "WarningLevel").text = "4" - -icon_property_group = ET.SubElement(root, "PropertyGroup") -ET.SubElement(icon_property_group, "ApplicationIcon").text = "SANS_Icon.ico" - -references = [ - ("Microsoft.Office.Interop.Excel, Version=15.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c, processorArchitecture=MSIL", "True"), - "ReachFramework", - "System", - "System.Data", - "System.Xml", - "Microsoft.CSharp", - "System.Core", - "System.Xml.Linq", - "System.Data.DataSetExtensions", - ("System.Xaml", "4.0"), - "WindowsBase", - "PresentationCore", - "PresentationFramework" -] - -item_group = ET.SubElement(root, "ItemGroup") -for ref in references: - if isinstance(ref, tuple): - reference = ET.SubElement(item_group, "Reference", Include=ref[0]) - ET.SubElement(reference, "EmbedInteropTypes").text = ref[1] - else: - ET.SubElement(item_group, "Reference", Include=ref) - -compile_files = [ - "Models\\DataCollection.cs", - "Models\\InstrumentInfo.cs", - "IO\\CsvExporter.cs", - "Dialogs\\PreviewDialog.xaml.cs", - "IO\\ExcelIO.cs", - "Models\\A2Setting.cs", - "Models\\Estimation.cs", - "Models\\Geometry.cs", - "Models\\RunOrder.cs", - "EstimationPanel.xaml.cs", - "Models\\Experiment.cs", - "Models\\ExperimentSettings.cs", - "Models\\WaitForUnits.cs", - "Scripting\\GeniePythonScriptGenerator.cs", - "Scripting\\IScriptGenerator.cs", - "Scripting\\OpenGenieScriptGenerator.cs", - "SettingsPanel.xaml.cs", - "ViewModels\\DataGridVM.cs", - "ViewModels\\EstimationPanelVM.cs", - "ViewModels\\InstrumentVM.cs", - "ViewModels\\SettingsPanelVM.cs", - "App.xaml.cs", - "MainWindow.xaml.cs", - "Properties\\AssemblyInfo.cs", - "Properties\\Resources.Designer.cs", - "Properties\\Settings.Designer.cs" -] - -for file in compile_files: - compile = ET.SubElement(item_group, "Compile", Include=file) - if file.endswith(".xaml.cs"): - ET.SubElement(compile, "DependentUpon").text = file.replace(".cs", "") - elif file.endswith(".Designer.cs"): - ET.SubElement(compile, "AutoGen").text = "True" - ET.SubElement(compile, "DesignTime").text = "True" - ET.SubElement(compile, "DependentUpon").text = file.replace(".Designer.cs", ".resx") - if "Settings" in file: - ET.SubElement(compile, "DesignTimeSharedInput").text = "True" - -pages = [ - "Dialogs\\PreviewDialog.xaml", - "EstimationPanel.xaml", - "MainWindow.xaml", - "SettingsPanel.xaml" -] - -for page in pages: - page_element = ET.SubElement(item_group, "Page", Include=page) - ET.SubElement(page_element, "SubType").text = "Designer" - ET.SubElement(page_element, "Generator").text = "MSBuild:Compile" - -embedded_resources = [ - "Properties\\Resources.resx" -] - -for resource in embedded_resources: - embedded_resource = ET.SubElement(item_group, "EmbeddedResource", Include=resource) - ET.SubElement(embedded_resource, "Generator").text = "ResXFileCodeGenerator" - ET.SubElement(embedded_resource, "LastGenOutput").text = resource.replace(".resx", ".Designer.cs") - -none_files = [ - "Properties\\Settings.settings", - "App.config" -] - -for none_file in none_files: - none = ET.SubElement(item_group, "None", Include=none_file) - if none_file.endswith(".settings"): - ET.SubElement(none, "Generator").text = "SettingsSingleFileGenerator" - ET.SubElement(none, "LastGenOutput").text = none_file.replace(".settings", ".Designer.cs") - -ET.SubElement(item_group, "AppDesigner", Include="Properties\\") - -bootstrapper_packages = [ - (".NETFramework,Version=v4.5", "Microsoft .NET Framework 4.5 %28x86 and x64%29", "true"), - ("Microsoft.Net.Framework.3.5.SP1", ".NET Framework 3.5 SP1", "false") -] - -for package in bootstrapper_packages: - bootstrapper_package = ET.SubElement(item_group, "BootstrapperPackage", Include=package[0]) - ET.SubElement(bootstrapper_package, "Visible").text = "False" - ET.SubElement(bootstrapper_package, "ProductName").text = package[1] - ET.SubElement(bootstrapper_package, "Install").text = package[2] - -ET.SubElement(item_group, "Resource", Include="SANS_Icon.ico") - -ET.SubElement(item_group, "WCFMetadata", Include="Connected Services\\") - -ET.SubElement(root, "Import", Project="$(MSBuildToolsPath)\\Microsoft.CSharp.targets") - -tree = ET.ElementTree(root) -tree.write("SANS_Script_GUI_v4.xml", encoding="utf-8", xml_declaration=True) -``` - -This Python script uses the `xml.etree.ElementTree` module to create an XML structure similar to the provided `.csproj` file. The resulting XML file will be saved as `SANS_Script_GUI_v4.xml`. - -dehoni: Please translate the selected code into python and use approbriate packages. - -GitHub Copilot: Here is the translated code into Python using the `tkinter` package: - -```python - - -import tkinter as tk -from tkinter import ttk, filedialog, messagebox -from tkinter.scrolledtext import ScrolledText - -class MainWindow(tk.Tk): - def __init__(self): - super().__init__() - - self.title("SANS Script GUI") - self.geometry("800x600") - - self.data = DataGridVM() - self.inst_info = InstrumentInfo() - self.settings = SettingsPanelVM(self.inst_info) - self.estimate = Estimation() - self.instrument = InstrumentVM(self.settings.experiment, self.estimate) - - self.create_widgets() - self.load_last_instrument() - - def create_widgets(self): - self.data_grid = ttk.Treeview(self) - self.data_grid.pack(fill=tk.BOTH, expand=True) - - self.pnl_settings = SettingsPanel(self, self.settings) - self.pnl_settings.pack(fill=tk.X) - self.pnl_settings.on_preview_clicked = self.pnl_settings_on_preview_clicked - self.pnl_settings.on_write_clicked = self.pnl_settings_on_write_clicked - - self.pnl_estimate = EstimationPanel(self, self.estimate) - self.pnl_estimate.pack(fill=tk.X) - self.pnl_estimate.on_calculate_clicked = self.pnl_estimate_on_calculate_clicked - - self.btn_create_new_table = ttk.Button(self, text="Create New Table", command=self.btn_create_new_table_click) - self.btn_create_new_table.pack(side=tk.LEFT, padx=5, pady=5) - - self.btn_open_excel = ttk.Button(self, text="Open Excel", command=self.btn_open_excel_click) - self.btn_open_excel.pack(side=tk.LEFT, padx=5, pady=5) - - self.mnu_main = tk.Menu(self) - self.config(menu=self.mnu_main) - - file_menu = tk.Menu(self.mnu_main, tearoff=0) - self.mnu_main.add_cascade(label="File", menu=file_menu) - file_menu.add_command(label="Export to Excel", command=self.mnu_export_to_excel_click) - file_menu.add_command(label="Save as CSV", command=self.mnu_save_csv_click) - file_menu.add_separator() - file_menu.add_command(label="Exit", command=self.mnu_exit_click) - - def load_last_instrument(self): - inst = "LARMOR" # Replace with actual settings retrieval - if inst == "LARMOR": - self.instrument.is_larmor = True - elif inst == "LOQ": - self.instrument.is_loq = True - elif inst == "SANS2D": - self.instrument.is_sans2d = True - elif inst == "ZOOM": - self.instrument.is_zoom = True - else: - self.instrument.is_larmor = True - - def pnl_settings_on_write_clicked(self): - filetypes = [("genie_python Files", "*.py")] if self.instrument.is_larmor or self.instrument.is_loq or self.instrument.is_zoom or self.instrument.is_sans2d else [("Open Genie Files", "*.gcl")] - filepath = filedialog.asksaveasfilename(defaultextension=filetypes[0][1], filetypes=filetypes) - if filepath: - scriptname = filepath.split("/")[-1].replace(" ", "_").rsplit(".", 1)[0] - script = self.inst_info.script_generator.generate_script(self.data.runs, self.settings.experiment, scriptname) - with open(filepath, "w") as file: - file.write(script) - - def pnl_settings_on_preview_clicked(self): - script = self.inst_info.script_generator.generate_script(self.data.runs, self.settings.experiment, "my_script") - preview_dialog = PreviewDialog(self, script) - preview_dialog.show() - - def pnl_estimate_on_calculate_clicked(self): - self.estimate.script_time = self.inst_info.script_generator.estimate_run_time(self.data.runs, self.settings.experiment, self.estimate) - - def btn_create_new_table_click(self): - self.data.runs.clear() - - def btn_open_excel_click(self): - filepath = filedialog.askopenfilename(defaultextension=".xlsx", filetypes=[("Excel Files", "*.xls;*.xlsx")]) - if filepath: - try: - excel = ExcelIO() - excel.open_excel_workbook(filepath) - exps = excel.extract_experiments() - self.data.runs = exps - except Exception as err: - messagebox.showerror("SANS Script - Error", "Could not open Excel spreadsheet") - - def mnu_exit_click(self): - self.destroy() - - def mnu_export_to_excel_click(self): - excel = ExcelIO() - excel.export_data_to_excel(self.data.runs) - - def mnu_save_csv_click(self): - filepath = filedialog.asksaveasfilename(defaultextension=".csv", filetypes=[("CSV File", "*.csv")]) - if filepath: - try: - CsvExporter.export(filepath, self.data.runs) - except Exception as err: - messagebox.showerror("SANS Script - Error", "Could not save as CSV") - - def on_close(self): - if self.instrument.is_larmor: - last_instrument = "LARMOR" - elif self.instrument.is_loq: - last_instrument = "LOQ" - elif self.instrument.is_sans2d: - last_instrument = "SANS2D" - elif self.instrument.is_zoom: - last_instrument = "ZOOM" - else: - last_instrument = "LARMOR" - # Save last_instrument to settings - self.destroy() - -class SettingsPanel(ttk.Frame): - def __init__(self, parent, settings_vm): - super().__init__(parent) - self.settings_vm = settings_vm - self.on_preview_clicked = None - self.on_write_clicked = None - self.create_widgets() - - def create_widgets(self): - self.preview_button = ttk.Button(self, text="Preview Script", command=self.preview_click) - self.preview_button.pack(side=tk.LEFT, padx=5, pady=5) - self.write_button = ttk.Button(self, text="Write Script", command=self.write_click) - self.write_button.pack(side=tk.LEFT, padx=5, pady=5) - - def preview_click(self): - if self.on_preview_clicked: - self.on_preview_clicked() - - def write_click(self): - if self.on_write_clicked: - self.on_write_clicked() - -class EstimationPanel(ttk.Frame): - def __init__(self, parent, estimate): - super().__init__(parent) - self.estimate = estimate - self.on_calculate_clicked = None - self.create_widgets() - - def create_widgets(self): - self.calculate_button = ttk.Button(self, text="Calculate", command=self.calculate_click) - self.calculate_button.pack(side=tk.LEFT, padx=5, pady=5) - - def calculate_click(self): - if self.on_calculate_clicked: - self.on_calculate_clicked() - -class PreviewDialog(tk.Toplevel): - def __init__(self, parent, script): - super().__init__(parent) - self.title("Preview Script") - self.geometry("600x400") - self.script_text = ScrolledText(self) - self.script_text.pack(fill=tk.BOTH, expand=True) - self.script_text.insert(tk.END, script) - self.script_text.config(state=tk.DISABLED) - - def show(self): - self.grab_set() - self.wait_window() - -class DataGridVM: - def __init__(self): - self.runs = [] - -class SettingsPanelVM: - def __init__(self, inst_info): - self.experiment = Experiment() - self.inst_info = inst_info - -class Estimation: - def __init__(self): - self.script_time = 0 - -class InstrumentInfo: - class ScriptGenerator: - @staticmethod - def generate_script(runs, experiment, scriptname): - return f"# Script for {scriptname}\n# Runs: {runs}\n# Experiment: {experiment}" - - @staticmethod - def estimate_run_time(runs, experiment, estimate): - return len(runs) * 10 # Dummy implementation - -class InstrumentVM: - def __init__(self, experiment, estimate): - self.is_larmor = False - self.is_loq = False - self.is_sans2d = False - self.is_zoom = False - self.experiment = experiment - self.estimate = estimate - -class ExcelIO: - def open_excel_workbook(self, filename): - pass # Dummy implementation - - def extract_experiments(self): - return [] # Dummy implementation - - def export_data_to_excel(self, runs): - pass # Dummy implementation - -class CsvExporter: - @staticmethod - def export(filename, runs): - pass # Dummy implementation - -class Experiment: - pass # Dummy implementation - -if __name__ == "__main__": - app = MainWindow() - app.protocol("WM_DELETE_WINDOW", app.on_close) - app.mainloop() -``` - -In this Python version: -- The - -MainWindow - - class inherits from `tk.Tk` and sets up the main window and its widgets. -- The `SettingsPanel` and `EstimationPanel` classes are custom frames for the settings and estimation panels. -- The - -PreviewDialog - - class is a custom dialog for previewing the script. -- The - -DataGridVM - -, - -SettingsPanelVM - -, - -Estimation - -, - -InstrumentInfo - -, - -InstrumentVM - -, - -ExcelIO - -, - -CsvExporter - -, and - -Experiment - - classes are placeholders for the corresponding functionality. -- The `if __name__ == "__main__":` block creates and runs the main application window. - -dehoni: Please translate the selected code into python and use approbriate packages. - -GitHub Copilot: Here is the translated code into Python using the `tkinter` package: - -```python - - -import tkinter as tk -from tkinter import ttk, Menu, filedialog, messagebox - -class MainWindow(tk.Tk): - def __init__(self): - super().__init__() - - self.title("SANS Script") - self.geometry("1400x650") - - self.create_widgets() - - def create_widgets(self): - self.grid_columnconfigure(1, weight=1) - self.grid_rowconfigure(4, weight=1) - - # Menu - self.mnu_main = Menu(self) - self.config(menu=self.mnu_main) - - file_menu = Menu(self.mnu_main, tearoff=0) - self.mnu_main.add_cascade(label="_File", menu=file_menu) - file_menu.add_command(label="Export Table to Excel", command=self.mnu_export_to_excel_click) - file_menu.add_command(label="Save as CSV", command=self.mnu_save_csv_click) - file_menu.add_separator() - file_menu.add_command(label="E_xit", command=self.mnu_exit_click) - - instrument_menu = Menu(self.mnu_main, tearoff=0) - self.mnu_main.add_cascade(label="_Instrument", menu=instrument_menu) - self.is_larmor = tk.BooleanVar() - self.is_loq = tk.BooleanVar() - self.is_sans2d = tk.BooleanVar() - self.is_zoom = tk.BooleanVar() - instrument_menu.add_checkbutton(label="LARMOR", variable=self.is_larmor) - instrument_menu.add_checkbutton(label="LOQ", variable=self.is_loq) - instrument_menu.add_checkbutton(label="SANS2D", variable=self.is_sans2d) - instrument_menu.add_checkbutton(label="ZOOM", variable=self.is_zoom) - - # Buttons - button_frame = ttk.Frame(self) - button_frame.grid(row=1, column=0, columnspan=3, pady=10) - self.btn_open_excel = ttk.Button(button_frame, text="Load Excel File", command=self.btn_open_excel_click) - self.btn_open_excel.pack(side=tk.LEFT, padx=5) - self.btn_create_new_table = ttk.Button(button_frame, text="Create New Table", command=self.btn_create_new_table_click) - self.btn_create_new_table.pack(side=tk.LEFT, padx=5) - - # Settings Panel - self.pnl_settings = SettingsPanel(self) - self.pnl_settings.grid(row=2, column=0, columnspan=3, sticky="ew") - - # Estimation Panel - self.pnl_estimate = EstimationPanel(self) - self.pnl_estimate.grid(row=3, column=0, columnspan=3, sticky="ew") - - # Data Grid - self.data_grid = ttk.Treeview(self, columns=("POSITION", "TRANS", "TRANS__WAIT", "SANS", "SANS__WAIT", "PERIOD", "SAMPLE__IDENTIFICATION__STRING", "BACKGROUND", "THICKNESS", "TEMPERATURE", "TEMPERATURE2", "FIELD", "SHEAR__RATE__1", "SHEAR__RATE__2", "SHEAR__ANGLE__1", "SHEAR__ANGLE__2", "PRE__COMMAND", "POST__COMMAND", "RB__NUMBER"), show="headings") - self.data_grid.grid(row=1, column=1, rowspan=4, columnspan=2, sticky="nsew", padx=5, pady=5) - for col in self.data_grid["columns"]: - self.data_grid.heading(col, text=col.replace("__", " ")) - - def btn_open_excel_click(self): - filepath = filedialog.askopenfilename(defaultextension=".xlsx", filetypes=[("Excel Files", "*.xls;*.xlsx")]) - if filepath: - try: - excel = ExcelIO() - excel.open_excel_workbook(filepath) - exps = excel.extract_experiments() - self.data_grid.delete(*self.data_grid.get_children()) - for exp in exps: - self.data_grid.insert("", "end", values=exp) - except Exception as err: - messagebox.showerror("SANS Script - Error", "Could not open Excel spreadsheet") - - def btn_create_new_table_click(self): - self.data_grid.delete(*self.data_grid.get_children()) - - def mnu_export_to_excel_click(self): - excel = ExcelIO() - excel.export_data_to_excel(self.data_grid.get_children()) - - def mnu_save_csv_click(self): - filepath = filedialog.asksaveasfilename(defaultextension=".csv", filetypes=[("CSV File", "*.csv")]) - if filepath: - try: - CsvExporter.export(filepath, self.data_grid.get_children()) - except Exception as err: - messagebox.showerror("SANS Script - Error", "Could not save as CSV") - - def mnu_exit_click(self): - self.destroy() - -class SettingsPanel(ttk.Frame): - def __init__(self, parent): - super().__init__(parent) - self.create_widgets() - - def create_widgets(self): - self.preview_button = ttk.Button(self, text="Preview Script", command=self.preview_click) - self.preview_button.pack(side=tk.LEFT, padx=5, pady=5) - self.write_button = ttk.Button(self, text="Write Script", command=self.write_click) - self.write_button.pack(side=tk.LEFT, padx=5, pady=5) - - def preview_click(self): - pass # Implement preview click functionality - - def write_click(self): - pass # Implement write click functionality - -class EstimationPanel(ttk.Frame): - def __init__(self, parent): - super().__init__(parent) - self.create_widgets() - - def create_widgets(self): - self.calculate_button = ttk.Button(self, text="Calculate", command=self.calculate_click) - self.calculate_button.pack(side=tk.LEFT, padx=5, pady=5) - - def calculate_click(self): - pass # Implement calculate click functionality - -class ExcelIO: - def open_excel_workbook(self, filename): - pass # Dummy implementation - - def extract_experiments(self): - return [] # Dummy implementation - - def export_data_to_excel(self, runs): - pass # Dummy implementation - -class CsvExporter: - @staticmethod - def export(filename, runs): - pass # Dummy implementation - -if __name__ == "__main__": - app = MainWindow() - app.mainloop() -``` - -In this Python version: -- The `MainWindow` class inherits from `tk.Tk` and sets up the main window and its widgets. -- The `SettingsPanel` and `EstimationPanel` classes are custom frames for the settings and estimation panels. -- The `ExcelIO` and `CsvExporter` classes are placeholders for the corresponding functionality. -- The `if __name__ == "__main__":` block creates and runs the main application window. - -dehoni: Please translate the selected code into python and use approbriate packages. - -GitHub Copilot: Here is the translated code into Python using the `tkinter` package: - -```python - - -import tkinter as tk -from tkinter import ttk - -class EstimationPanel(ttk.Frame): - def __init__(self, parent): - super().__init__(parent) - self.on_calculate_clicked = None - self.create_widgets() - - def create_widgets(self): - self.calculate_button = ttk.Button(self, text="Calculate", command=self.btn_calculate_click) - self.calculate_button.pack(padx=5, pady=5) - - def btn_calculate_click(self): - if self.on_calculate_clicked is not None: - self.on_calculate_clicked() - -if __name__ == "__main__": - root = tk.Tk() - panel = EstimationPanel(root) - panel.pack() - root.mainloop() -``` - -In this Python version: -- The - -EstimationPanel - - class inherits from `ttk.Frame`. -- The `on_calculate_clicked` attribute is used to store a callback function. -- The `create_widgets` method sets up the UI elements. -- The `btn_calculate_click` method handles the button click event and calls the callback if it is set. -- The `if __name__ == "__main__":` block creates a simple application to demonstrate the - -EstimationPanel - - usage. - -dehoni: Please translate the selected code into python and use approbriate packages. - -GitHub Copilot: Here is the translated code into Python using the `tkinter` package: - -```python - - -import tkinter as tk -from tkinter import ttk - -class EstimationPanel(ttk.Frame): - def __init__(self, parent, view_model): - super().__init__(parent) - self.view_model = view_model - self.on_calculate_clicked = None - self.create_widgets() - - def create_widgets(self): - self.pack(fill=tk.BOTH, expand=True) - - label = ttk.Label(self, text="Estimated Script Time", font=("Helvetica", 16, "bold")) - label.pack(pady=10) - - grid_frame = ttk.Frame(self) - grid_frame.pack(padx=10, pady=10) - - # Est. count rate - ttk.Label(grid_frame, text="Est. count rate:").grid(row=0, column=0, sticky=tk.E) - count_rate_frame = ttk.Frame(grid_frame) - count_rate_frame.grid(row=0, column=1, columnspan=2) - self.txt_count_rate = ttk.Entry(count_rate_frame, width=10, textvariable=self.view_model.count_rate) - self.txt_count_rate.pack(side=tk.LEFT, padx=5, pady=5) - ttk.Label(count_rate_frame, text="µA/hr").pack(side=tk.LEFT) - - # Est. time between moves - ttk.Label(grid_frame, text="Est. time between moves:").grid(row=1, column=0, sticky=tk.E) - move_time_frame = ttk.Frame(grid_frame) - move_time_frame.grid(row=1, column=1, columnspan=2) - self.txt_move_time = ttk.Entry(move_time_frame, width=10, textvariable=self.view_model.move_time) - self.txt_move_time.pack(side=tk.LEFT, padx=5, pady=5) - ttk.Label(move_time_frame, text="seconds").pack(side=tk.LEFT) - - # Est. script time - ttk.Label(grid_frame, text="Est. script time:").grid(row=2, column=0, sticky=tk.E) - script_time_frame = ttk.Frame(grid_frame) - script_time_frame.grid(row=2, column=1, columnspan=2) - self.txt_estimate = ttk.Entry(script_time_frame, width=15, textvariable=self.view_model.script_time, state="readonly") - self.txt_estimate.pack(side=tk.LEFT, padx=5, pady=5) - - # Calculate button - self.btn_calculate = ttk.Button(grid_frame, text="Calculate", command=self.btn_calculate_click) - self.btn_calculate.grid(row=3, column=1, columnspan=2, pady=10) - - def btn_calculate_click(self): - if self.on_calculate_clicked is not None: - self.on_calculate_clicked() - -class EstimationPanelViewModel: - def __init__(self): - self.count_rate = tk.StringVar() - self.move_time = tk.StringVar() - self.script_time = tk.StringVar() - -if __name__ == "__main__": - root = tk.Tk() - view_model = EstimationPanelViewModel() - panel = EstimationPanel(root, view_model) - panel.pack(fill=tk.BOTH, expand=True) - root.mainloop() -``` - -In this Python version: -- The `EstimationPanel` class inherits from `ttk.Frame`. -- The `EstimationPanelViewModel` class is used to bind data to the UI elements. -- The `create_widgets` method sets up the UI elements similar to the XAML layout. -- The `btn_calculate_click` method handles the button click event and calls the callback if it is set. -- The `if __name__ == "__main__":` block creates a simple application to demonstrate the `EstimationPanel` usage. - -dehoni: Please translate the selected code into python and use approbriate packages. - -GitHub Copilot: Here is the translated code into Python using the `tkinter` package: - -```python - - -import tkinter as tk -from MainWindow import MainWindow - -class App: - def __init__(self): - self.root = tk.Tk() - self.main_window = MainWindow(self.root) - - def run(self): - self.root.mainloop() - -if __name__ == "__main__": - app = App() - app.run() -``` - -In this Python version: -- The - -App - - class initializes the main application window using `tkinter`. -- The `run` method starts the main event loop. -- The `if __name__ == "__main__":` block creates an instance of the - -App - - class and runs the application. - -dehoni: Please translate the selected code into python and use approbriate packages. - -GitHub Copilot: Here is the translated code into Python using the `tkinter` package: - -```python - - -import tkinter as tk -from MainWindow import MainWindow - -class App: - def __init__(self): - self.root = tk.Tk() - self.main_window = MainWindow(self.root) - self.main_window.pack(fill=tk.BOTH, expand=True) - - def run(self): - self.root.mainloop() - -if __name__ == "__main__": - app = App() - app.run() -``` - -In this Python version: -- The `App` class initializes the main application window using `tkinter`. -- The `MainWindow` class is imported and instantiated, and it is packed to fill the main window. -- The `run` method starts the main event loop. -- The `if __name__ == "__main__":` block creates an instance of the `App` class and runs the application. - -dehoni: Please translate the selected code into python and use approbriate packages. - -GitHub Copilot: Here is the translated code into Python using the `configparser` package: - -```python - - -import configparser - -config = configparser.ConfigParser() - -config["DEFAULT"] = { - "LastInstrument": "LARMOR", - "SampleHeight": "11", - "SampleWidth": "11", - "Geometry": "Flat Plate", - "RunOrder": "Alternate - TRANS first", - "NumTrans": "1", - "NumSans": "1", - "LoopPerRun": "False", - "A2SettingSans": "Large = 25, 16 mm", - "A2SettingTrans": "Large = 25, 16 mm", - "CollectionMode": "Events" -} - -with open("app_config.ini", "w") as configfile: - config.write(configfile) -``` - -In this Python version: -- The `configparser` module is used to create and manage the configuration settings. -- The settings from the XML file are added to the `DEFAULT` section of the configuration. -- The configuration is written to a file named `app_config.ini`. - -dehoni: Please translate the selected code into python and use approbriate packages. - -GitHub Copilot: Here is the translated code into Python using the `tkinter` package and the `tkinter` variable classes for data binding: - -```python - - -import tkinter as tk -from tkinter import StringVar -from tkinter import ttk - -class DataGridVM: - def __init__(self): - self._runs = tk.Variable(value=[]) - self.property_changed = [] - - @property - def runs(self): - return self._runs.get() - - @runs.setter - def runs(self, value): - self._runs.set(value) - self.on_property_changed("runs") - - @staticmethod - def wait_for_choices(): - return WaitForUnits.choices() - - def on_property_changed(self, name): - for callback in self.property_changed: - callback(name) - -class WaitForUnits: - @staticmethod - def choices(): - return ["Choice1", "Choice2", "Choice3"] - -class Experiment: - pass # Dummy implementation - -if __name__ == "__main__": - root = tk.Tk() - vm = DataGridVM() - - def on_property_changed(name): - print(f"Property {name} changed") - - vm.property_changed.append(on_property_changed) - - vm.runs = [Experiment(), Experiment()] - print(vm.runs) - - root.mainloop() -``` - -In this Python version: -- The - -DataGridVM - - class uses `tk.Variable` for data binding. -- The - -runs - - property is implemented with getter and setter methods to handle property changes. -- The `on_property_changed` method notifies subscribers when a property changes. -- The - -WaitForUnits - - class provides static choices for the `wait_for_choices` method. -- The - -Experiment - - class is a placeholder for the actual implementation. -- The `if __name__ == "__main__":` block demonstrates how to use the - -DataGridVM - - class and handle property changes. - -dehoni: Please translate the selected code into python and use approbriate packages. - -GitHub Copilot: Here is the translated code into Python using the `tkinter` package and the `tkinter` variable classes for data binding: - -```python - - -import tkinter as tk - -class EstimationPanelVM: - def __init__(self): - self.property_changed = [] - - def on_property_changed(self, name): - for callback in self.property_changed: - callback(name) - -if __name__ == "__main__": - root = tk.Tk() - vm = EstimationPanelVM() - - def on_property_changed(name): - print(f"Property {name} changed") - - vm.property_changed.append(on_property_changed) - - # Example of triggering property change - vm.on_property_changed("example_property") - - root.mainloop() -``` - -In this Python version: -- The - -EstimationPanelVM - - class uses a list to store property change callbacks. -- The `on_property_changed` method notifies subscribers when a property changes. -- The `if __name__ == "__main__":` block demonstrates how to use the - -EstimationPanelVM - - class and handle property changes. - -dehoni: Please translate the selected code into python and use approbriate packages. - -GitHub Copilot: Here is the translated code into Python using the `tkinter` package and the `tkinter` variable classes for data binding: - -```python - - -import tkinter as tk -from tkinter import StringVar, BooleanVar - -class InstrumentVM: - def __init__(self, settings, estimation): - self.settings = settings - self.estimation = estimation - - self.loq_aperture_sizes = [A2Setting.Large, A2Setting.Medium, A2Setting.Small] - self.sans2d_aperture_sizes = [A2Setting.XLarge, A2Setting.Large, A2Setting.Medium, A2Setting.Small] - self.larmor_aperture_sizes = [A2Setting.Larmor] - self.zoom_aperture_sizes = [A2Setting.Large, A2Setting.Medium, A2Setting.Small] - - self.is_larmor = BooleanVar(value=True) - self.is_loq = BooleanVar(value=False) - self.is_sans2d = BooleanVar(value=False) - self.is_zoom = BooleanVar(value=False) - - self.is_larmor.trace_add("write", self.update_instrument) - self.is_loq.trace_add("write", self.update_instrument) - self.is_sans2d.trace_add("write", self.update_instrument) - self.is_zoom.trace_add("write", self.update_instrument) - - self.update_instrument() - - def update_instrument(self, *args): - if self.is_larmor.get(): - self.set_instrument(True, False, False, False, "A1, S1 Setting:", self.larmor_aperture_sizes, 40) - elif self.is_loq.get(): - self.set_instrument(False, True, False, False, "A2 Setting:", self.loq_aperture_sizes, 170) - elif self.is_sans2d.get(): - self.set_instrument(False, False, True, False, "A6 Setting:", self.sans2d_aperture_sizes, 40) - elif self.is_zoom.get(): - self.set_instrument(False, False, False, True, "A6 Setting:", self.zoom_aperture_sizes, 40) - - def set_instrument(self, larmor, loq, sans2d, zoom, a2_label, a2_choices, count_rate): - self.is_larmor.set(larmor) - self.is_loq.set(loq) - self.is_sans2d.set(sans2d) - self.is_zoom.set(zoom) - - InstrumentInfo.script_generator = Scripting.GeniePythonScriptGenerator(larmor, loq, zoom, sans2d) - InstrumentInfo.a2_label = a2_label - InstrumentInfo.a2_choices = a2_choices - InstrumentInfo.a2_trans_enabled = True - InstrumentInfo.collection_mode_enabled = True - self.estimation.count_rate = count_rate - self.settings.a2_setting_sans = Properties.Settings.Default.a2_setting_sans - self.settings.a2_setting_trans = Properties.Settings.Default.a2_setting_trans - -class A2Setting: - Large = "Large" - Medium = "Medium" - Small = "Small" - XLarge = "XLarge" - Larmor = "Larmor" - -class InstrumentInfo: - script_generator = None - a2_label = "" - a2_choices = [] - a2_trans_enabled = False - collection_mode_enabled = False - -class Scripting: - class GeniePythonScriptGenerator: - def __init__(self, larmor, loq, zoom, sans2d): - pass # Dummy implementation - -class Properties: - class Settings: - class Default: - a2_setting_sans = "Large" - a2_setting_trans = "Large" - -class ExperimentSettings: - def __init__(self): - self.a2_setting_sans = "" - self.a2_setting_trans = "" - -class Estimation: - def __init__(self): - self.count_rate = 0 - -if __name__ == "__main__": - root = tk.Tk() - settings = ExperimentSettings() - estimation = Estimation() - vm = InstrumentVM(settings, estimation) - - def on_property_changed(name): - print(f"Property {name} changed") - - vm.is_larmor.trace_add("write", lambda *args: on_property_changed("is_larmor")) - vm.is_loq.trace_add("write", lambda *args: on_property_changed("is_loq")) - vm.is_sans2d.trace_add("write", lambda *args: on_property_changed("is_sans2d")) - vm.is_zoom.trace_add("write", lambda *args: on_property_changed("is_zoom")) - - root.mainloop() -``` - -In this Python version: -- The - -InstrumentVM - - class uses `BooleanVar` for data binding. -- The `update_instrument` method updates the instrument settings based on the selected instrument. -- The `set_instrument` method sets the instrument-specific properties. -- The - -A2Setting - -, - -InstrumentInfo - -, - -Scripting - -, - -Properties - -, - -ExperimentSettings - -, and - -Estimation - - classes are placeholders for the actual implementation. -- The `if __name__ == "__main__":` block demonstrates how to use the - -InstrumentVM - - class and handle property changes. - -dehoni: Please translate the selected code into python and use approbriate packages. - -GitHub Copilot: Here is the translated code into Python using the `tkinter` package and the `tkinter` variable classes for data binding: - -```python - - -import tkinter as tk -from tkinter import StringVar - -class SettingsPanelVM: - def __init__(self, inst_info=None): - self.property_changed = [] - - self._experiment = ExperimentSettings() - self._inst_info = inst_info if inst_info else InstrumentInfo() - - def on_property_changed(self, name): - for callback in self.property_changed: - callback(name) - - @property - def experiment(self): - return self._experiment - - @experiment.setter - def experiment(self, value): - self._experiment = value - self.on_property_changed("experiment") - - @property - def inst_info(self): - return self._inst_info - - @inst_info.setter - def inst_info(self, value): - self._inst_info = value - self.on_property_changed("inst_info") - - @staticmethod - def order_choices(): - return RunOrder.choices() - - @staticmethod - def geometry_choices(): - return Geometry.choices() - - @staticmethod - def collection_mode_choices(): - return DataCollection.choices() - -class RunOrder: - @staticmethod - def choices(): - return ["Choice1", "Choice2", "Choice3"] - -class Geometry: - @staticmethod - def choices(): - return ["Geometry1", "Geometry2", "Geometry3"] - -class DataCollection: - @staticmethod - def choices(): - return ["Mode1", "Mode2", "Mode3"] - -class ExperimentSettings: - pass # Dummy implementation - -class InstrumentInfo: - pass # Dummy implementation - -if __name__ == "__main__": - root = tk.Tk() - vm = SettingsPanelVM() - - def on_property_changed(name): - print(f"Property {name} changed") - - vm.property_changed.append(on_property_changed) - - vm.experiment = ExperimentSettings() - vm.inst_info = InstrumentInfo() - - root.mainloop() -``` - -In this Python version: -- The - -SettingsPanelVM - - class uses a list to store property change callbacks. -- The `on_property_changed` method notifies subscribers when a property changes. -- The - -experiment - - and `inst_info` properties are implemented with getter and setter methods to handle property changes. -- The `order_choices`, `geometry_choices`, and `collection_mode_choices` methods provide static choices for the combo boxes. -- The - -RunOrder - -, - -Geometry - -, - -DataCollection - -, - -ExperimentSettings - -, and - -InstrumentInfo - - classes are placeholders for the actual implementation. -- The `if __name__ == "__main__":` block demonstrates how to use the - -SettingsPanelVM - - class and handle property changes. - -dehoni: Please translate the selected code into python and use approbriate packages. - -GitHub Copilot: Here is the translated code into Python: - -```python - - -from datetime import datetime, timedelta - -class GeniePythonScriptGenerator: - def __init__(self, is_larmor, is_loq, is_zoom, is_sans2d): - self.indent = " " - self.is_larmor = is_larmor - self.is_loq = is_loq - self.is_zoom = is_zoom - self.is_sans2d = is_sans2d - - def generate_script(self, runs, settings, scriptname): - script = f"# Script created by SANSScript at {datetime.now()}\n\n" - script += self.generate_header(scriptname) - script += self.generate_setup(settings) - script += self.generate_runs(runs, settings) - script += "\n" - return script - - def get_indent_string(self, n): - return self.indent * n - - def generate_header(self, scriptname): - script = "import sys\n" - script += "from genie_python import genie as g, BLOCK_NAMES as b\n" - script += "sys.path.append(r\"c:\\instrument\\scripts\")\n" - - if self.is_larmor: - script += "from instrument.larmor import * # pylint: disable=wildcard-import, unused-wildcard-import\n" - if self.is_loq: - script += "from instrument.loq import * # pylint: disable=wildcard-import, unused-wildcard-import\n" - if self.is_zoom: - script += "from instrument.zoom import * # pylint: disable=wildcard-import, unused-wildcard-import\n" - if self.is_sans2d: - script += "from instrument.sans2d import * # pylint: disable=wildcard-import, unused-wildcard-import\n" - - script += f"\ndef {scriptname}():\n" - return script - - def generate_setup(self, settings): - script = "" - script += self.get_indent_string(1) + f"g.change_sample_par("Width", "{settings.sample_width}")\n" - script += self.get_indent_string(1) + f"g.change_sample_par("height", "{settings.sample_height}")\n" - script += self.get_indent_string(1) + f"g.change_sample_par("Geometry", "{settings.sample_geometry}")\n\n" - return script - - def check_positions(self, runs): - script = "" - if self.is_larmor: - position_list = ",".join([exp.position for exp in runs if exp.position]) - if position_list: - script += self.get_indent_string(1) + f"if check_move_pos(["{position_list}"]) != True:\n" - script += self.get_indent_string(2) + "print("Invalid positions - script aborted")\n" - script += self.get_indent_string(2) + "return\n\n" - return script - - def generate_runs(self, runs, settings): - script = self.check_positions(runs) - if not runs: - return script - - if settings.order == "AllTrans": - script += self.get_indent_string(1) + f"for i in range({settings.num_trans}):\n" - for exp in runs: - if exp.trans: - script = self.do_trans_run(script, exp, 2, settings) - script += self.get_indent_string(1) + f"for i in range({settings.num_sans}):\n" - for exp in runs: - if exp.sans: - script = self.do_sans_run(script, exp, 2, settings) - elif settings.order == "AllSans": - script += self.get_indent_string(1) + f"for i in range({settings.num_sans}):\n" - for exp in runs: - if exp.sans: - script = self.do_sans_run(script, exp, 2, settings) - if settings.num_trans and any(exp.trans for exp in runs): - script += self.get_indent_string(1) + f"for i in range({settings.num_trans}):\n" - for exp in runs: - if exp.trans: - script = self.do_trans_run(script, exp, 2, settings) - else: - script += self.get_indent_string(1) + f"num_sans = {settings.num_sans}\n" - script += self.get_indent_string(1) + f"num_trans = {settings.num_trans}\n" - script += self.get_indent_string(1) + "count = 0\n" - script += self.get_indent_string(1) + "while True:\n" - indent = 2 - for exp in runs: - if settings.order == "TransFirst": - if exp.trans: - script += self.get_indent_string(indent) + "if count < num_trans:\n" - script = self.do_trans_run(script, exp, indent + 1, settings) - if exp.sans: - script += self.get_indent_string(indent) + "if count < num_sans:\n" - script = self.do_sans_run(script, exp, indent + 1, settings) - else: - if exp.sans: - script += self.get_indent_string(indent) + "if count < num_sans:\n" - script = self.do_sans_run(script, exp, indent + 1, settings) - if exp.trans: - script += self.get_indent_string(indent) + "if count < num_trans:\n" - script = self.do_trans_run(script, exp, indent + 1, settings) - script += self.get_indent_string(indent) + "count += 1\n" - script += self.get_indent_string(indent) + "if count >= num_trans and count >= num_sans: break\n" - return script - - def do_trans_run(self, script, exp, indent, settings): - script += self.do_sample_environment(exp, indent) - waitfor = "uamps" - rtype = "histogram" - fulltitle = f"{{{exp.sample}}}" - if settings.collection_mode == "Events": - rtype = "event" - if exp.trans_wait == "Frames": - waitfor = "frames" - elif exp.trans_wait == "Seconds": - waitfor = "seconds" - elif exp.trans_wait == "Minutes": - waitfor = "minutes" - if self.is_larmor or self.is_zoom: - script += self.get_indent_string(indent) + f"do_trans(title="{fulltitle}", pos="{exp.position}", {waitfor}={exp.trans}, thickness={exp.thickness})\n" - elif self.is_loq: - script += self.get_indent_string(indent) + f"do_trans(title="{fulltitle}", pos="{exp.position}", {waitfor}={exp.trans}, thickness={exp.thickness}, aperture="small")\n" - else: - script += self.get_indent_string(indent) + f"do_trans(title="{fulltitle}", pos="{exp.position}", {waitfor}={exp.trans}, thickness={exp.thickness}, dae="transmission", aperture="{self.aperture_string(settings.a2_setting_trans)}")\n" - script += self.get_indent_string(indent) + exp.post_command + "\n" - return script - - def do_sans_run(self, script, exp, indent, settings): - script += self.do_sample_environment(exp, indent) - waitfor = "uamps" - rtype = "histogram" - fulltitle = f"{{{exp.sample}}}" - if exp.background: - fulltitle += f"_{{{exp.background}}}" - if settings.collection_mode in ["Events", "TRANSSANS_Events"]: - rtype = "event" - elif settings.collection_mode == "SESANS": - rtype = "sesans" - if settings.collection_mode in ["TRANSSANS_Events", "TRANSSANS_Histogram"]: - fulltitle += "_TRANS" - if exp.sans_wait == "Frames": - waitfor = "frames" - elif exp.sans_wait == "Seconds": - waitfor = "seconds" - elif exp.sans_wait == "Minutes": - waitfor = "minutes" - if self.is_larmor or self.is_zoom: - script += self.get_indent_string(indent) + f"do_sans(title="{fulltitle}", pos="{exp.position}", {waitfor}={exp.sans}, thickness={exp.thickness}, dae="{rtype}")\n" - else: - script += self.get_indent_string(indent) + f"do_sans(title="{fulltitle}", pos="{exp.position}", {waitfor}={exp.sans}, thickness={exp.thickness}, dae="{rtype}", aperture="{self.aperture_string(settings.a2_setting_sans)}")\n" - script += self.get_indent_string(indent) + exp.post_command + "\n" - return script - - def aperture_string(self, size): - if size in [A2Setting.Large, A2Setting.Larmor]: - return "large" - elif size in [A2Setting.Medium, A2Setting.Larmor]: - return "medium" - elif size in [A2Setting.Small, A2Setting.Larmor]: - return "small" - elif size in [A2Setting.XLarge, A2Setting.Larmor]: - return "xlarge" - return "" - - def do_sample_environment(self, exp, indent): - script = "" - if exp.temperature1: - temp1 = float(exp.temperature1) - temp1low = temp1 - 1.0 - temp1high = temp1 + 1.0 - if self.is_loq: - script += self.get_indent_string(indent) + f"g.cset(internal_setpoint_1={exp.temperature1})\n" - script += self.get_indent_string(indent) + f"g.cset(\"Julabo_1_ext\", lowlimit={temp1low}, highlimit={temp1high}, runcontrol=True)\n" - else: - script += self.get_indent_string(indent) + f"g.cset(JULABO1_SP={exp.temperature1})\n" - script += self.get_indent_string(indent) + f"g.cset(\"JULABO1_TEMP\", lowlimit={temp1low}, highlimit={temp1high}, runcontrol=True)\n" - if exp.temperature2: - temp2 = float(exp.temperature2) - temp2low = temp2 - 1.0 - temp2high = temp2 + 1.0 - if self.is_loq: - script += self.get_indent_string(indent) + f"g.cset(internal_setpoint_2={exp.temperature2})\n" - script += self.get_indent_string(indent) + f"g.cset(\"Julabo_2_ext\", lowlimit={temp2low}, highlimit={temp2high}, runcontrol=True)\n" - else: - script += self.get_indent_string(indent) + f"g.cset(JULABO2_SP={exp.temperature2})\n" - script += self.get_indent_string(indent) + f"g.cset(\"JULABO2_TEMP\", lowlimit={temp2low}, highlimit={temp2high}, runcontrol=True)\n" - if exp.pre_command: - script += self.get_indent_string(indent) + exp.pre_command + "\n" - if exp.field: - script += self.get_indent_string(indent) + f"g.cset(FIELD={exp.field})\n" - if exp.shear_rate1: - script += self.get_indent_string(indent) + f"g.cset(SHEAR_RATE_1={exp.shear_rate1})\n" - if exp.shear_rate2: - script += self.get_indent_string(indent) + f"g.cset(SHEAR_RATE_2={exp.shear_rate2})\n" - if exp.shear_angle1: - script += self.get_indent_string(indent) + f"g.cset(SHEAR_ANGLE_1={exp.shear_angle1})\n" - if exp.shear_angle2: - script += self.get_indent_string(indent) + f"g.cset(SHEAR_ANGLE_2={exp.shear_angle2})\n" - return script - - def estimate_run_time(self, runs, settings, estimation): - minutes = 0 - if settings.num_sans > 0: - minutes += self.calculate_time(runs, True, estimation.count_rate, estimation.move_time) * settings.num_sans - if settings.num_trans > 0: - minutes += self.calculate_time(runs, False, estimation.count_rate, estimation.move_time) * settings.num_trans - time = datetime(1, 1, 1, 0, 0, 0) + timedelta(minutes=minutes) - return time.strftime("%H:%M:%S") - - @staticmethod - def calculate_time(runs, is_sans, count_rate, seconds_between): - minutes = 0 - for exp in runs: - if exp.position: - if is_sans: - minutes += GeniePythonScriptGenerator.get_minutes(exp.sans_wait, exp.sans, count_rate) - else: - minutes += GeniePythonScriptGenerator.get_minutes(exp.trans_wait, exp.trans, count_rate) - minutes += seconds_between / 60 - return minutes - - @staticmethod - def get_minutes(wait, length, count_rate): - if wait == "Minutes": - return length - elif wait == "Seconds": - return length / 60.0 - elif wait == "Frames": - return length / (25.0 * 60.0) - else: - return length / (count_rate / 60.0) - -class A2Setting: - Large = "Large" - Medium = "Medium" - Small = "Small" - XLarge = "XLarge" - Larmor = "Larmor" - -class Experiment: - def __init__(self, position="", trans=0, sans=0, trans_wait="", sans_wait="", sample="", background="", thickness=0, pre_command="", post_command="", temperature1="", temperature2="", field="", shear_rate1="", shear_rate2="", shear_angle1="", shear_angle2=""): - self.position = position - self.trans = trans - self.sans = sans - self.trans_wait = trans_wait - self.sans_wait = sans_wait - self.sample = sample - self.background = background - self.thickness = thickness - self.pre_command = pre_command - self.post_command = post_command - self.temperature1 = temperature1 - self.temperature2 = temperature2 - self.field = field - self.shear_rate1 = shear_rate1 - self.shear_rate2 = shear_rate2 - self.shear_angle1 = shear_angle1 - self.shear_angle2 = shear_angle2 - -class ExperimentSettings: - def __init__(self, sample_width="", sample_height="", sample_geometry="", order="", num_trans=0, num_sans=0, collection_mode="", a2_setting_trans="", a2_setting_sans=""): - self.sample_width = sample_width - self.sample_height = sample_height - self.sample_geometry = sample_geometry - self.order = order - self.num_trans = num_trans - self.num_sans = num_sans - self.collection_mode = collection_mode - self.a2_setting_trans = a2_setting_trans - self.a2_setting_sans = a2_setting_sans - -class Estimation: - def __init__(self, count_rate=0, move_time=0): - self.count_rate = count_rate - self.move_time = move_time - -if __name__ == "__main__": - runs = [Experiment(position="pos1", trans=10, sans=20, trans_wait="Minutes", sans_wait="Minutes", sample="Sample1", thickness=1.0)] - settings = ExperimentSettings(sample_width="10", sample_height="10", sample_geometry="Flat", order="AllTrans", num_trans=1, num_sans=1, collection_mode="Events", a2_setting_trans="Large", a2_setting_sans="Large") - estimation = Estimation(count_rate=100, move_time=10) - generator = GeniePythonScriptGenerator(is_larmor=True, is_loq=False, is_zoom=False, is_sans2d=False) - script = generator.generate_script(runs, settings, "my_script") - print(script) - estimated_time = generator.estimate_run_time(runs, settings, estimation) - print(f"Estimated run time: {estimated_time}") -``` - -In this Python version: -- The - -GeniePythonScriptGenerator - - class is implemented with methods to generate the script and estimate run time. -- The - -A2Setting - -, - -Experiment - -, - -ExperimentSettings - -, and - -Estimation - - classes are implemented as placeholders for the actual data structures. -- The `if __name__ == "__main__":` block demonstrates how to use the - -GeniePythonScriptGenerator - - class to generate a script and estimate run time. - -dehoni: Please translate the selected code into python and use approbriate packages. - -GitHub Copilot: Here is the translated code into Python using an abstract base class: - -```python - - -from abc import ABC, abstractmethod - -class IScriptGenerator(ABC): - @abstractmethod - def generate_script(self, runs, settings, scriptname): - pass - - @abstractmethod - def estimate_run_time(self, runs, settings, estimation): - pass - -class Experiment: - pass # Dummy implementation - -class ExperimentSettings: - pass # Dummy implementation - -class Estimation: - pass # Dummy implementation -``` - -In this Python version: -- The - -IScriptGenerator - - class is defined as an abstract base class using the `abc` module. -- The `generate_script` and `estimate_run_time` methods are defined as abstract methods. -- The - -Experiment - -, - -ExperimentSettings - -, and - -Estimation - - classes are placeholders for the actual implementations. - -dehoni: Please translate the selected code into python and use approbriate packages. - -GitHub Copilot: Here is the translated code into Python: - -```python - - -from datetime import datetime, timedelta - -class OpenGenieScriptGenerator: - def __init__(self, is_sans2d): - self.is_sans2d = is_sans2d - - def generate_script(self, runs, settings, scriptname): - script = f"# Script created by LOQ Script at {datetime.now()}\n" - script += "# Script MUST be loaded in to Open GENIE using the loadscript command.\n\n" - - if self.is_sans2d: - script += "QUALIFIERS /test\n" - - script += f"LOCAL transCount = 0 sansCount = 0 numTRANS={settings.num_trans} numSANS={settings.num_sans}\n\n" - script += "SETSCRIPTNAME THIS_PROCEDURE()\n\n" - script += f"Sample_par width={settings.sample_width} height={settings.sample_height} geometry="{settings.sample_geometry}"\n\n" - - if self.is_sans2d: - position_list = ",".join([exp.position for exp in runs if exp.position]) - if position_list: - script += f"IF NOT CHECK_MOVE_POS("{position_list}")\n" - script += " PRINTEN "Invalid positions - script aborted"\n" - script += " RETURN\n" - script += "ENDIF\n\n" - script += "IF test\n" - script += " RETURN\n" - script += "ENDIF\n\n" - - if settings.order == "AllTrans": - if settings.loop_per_run: - script += self.do_all_trans_loop_each(runs, settings, True, True) - script += self.do_all_sans_loop_each(runs, settings, True, True) - else: - script += "LOOP\n\n" - script += self.do_all_trans(runs, settings, True, True) - script += self.do_all_sans(runs, settings, True, True) - script += "EXITIF ((transCount >= numTRANS) AND (sansCount >= numSANS))\n\n" - script += "ENDLOOP\n\n" - elif settings.order == "TransFirst": - if settings.loop_per_run: - for exp in runs: - script += self.do_single_trans_loop_each(exp, settings, True, False) - script += self.do_single_sans_loop_each(exp, settings, False, True) - else: - script += "LOOP\n\n" - for exp in runs: - script += self.do_single_trans(exp, settings, True, False) - script += self.do_single_sans(exp, settings, False, True) - script += "transCount = transCount + 1\n" - script += "sansCount = sansCount + 1\n\n" - script += "EXITIF ((transCount >= numTRANS) AND (sansCount >= numSANS))\n\n" - script += "ENDLOOP\n\n" - elif settings.order == "AllSans": - if settings.loop_per_run: - script += self.do_all_sans_loop_each(runs, settings, True, True) - script += self.do_all_trans_loop_each(runs, settings, True, True) - else: - script += "LOOP\n\n" - script += self.do_all_sans(runs, settings, True, True) - script += self.do_all_trans(runs, settings, True, True) - script += "EXITIF ((transCount >= numTRANS) AND (sansCount >= numSANS))\n\n" - script += "ENDLOOP\n\n" - elif settings.order == "SansFirst": - if settings.loop_per_run: - for exp in runs: - script += self.do_single_sans_loop_each(exp, settings, True, False) - script += self.do_single_trans_loop_each(exp, settings, False, True) - else: - script += "LOOP\n\n" - for exp in runs: - script += self.do_single_sans(exp, settings, True, False) - script += self.do_single_trans(exp, settings, False, True) - script += "transCount = transCount + 1\n" - script += "sansCount = sansCount + 1\n\n" - script += "EXITIF ((transCount >= numTRANS) AND (sansCount >= numSANS))\n\n" - script += "ENDLOOP\n\n" - - script += "PRINTN "SCRIPT COMPLETED "\n\n" - return script - - def do_single_sans(self, exp, settings, do_pre_com, do_post_com): - script = "" - if settings.num_sans > 0 and exp.sans > 0: - script += "IF (sansCount < numSANS)\n" - script += self.set_sans_aperture(settings.a2_setting_sans) - script += self.do_single_run(False, exp, False, do_pre_com, do_post_com, settings) - script += "\nENDIF\n\n" - return script - - def do_single_trans(self, exp, settings, do_pre_com, do_post_com): - script = "" - if settings.num_trans > 0 and exp.trans > 0: - script += "IF (transCount < numTRANS)\n" - script += self.set_trans_aperture(settings.a2_setting_trans) - script += self.do_single_run(True, exp, False, do_pre_com, do_post_com, settings) - script += "\nENDIF\n\n" - return script - - def do_single_sans_loop_each(self, exp, settings, do_pre_com, do_post_com): - script = "" - if settings.num_sans > 0 and exp.sans > 0: - script += self.set_sans_aperture(settings.a2_setting_sans) - script += self.do_single_run(False, exp, True, do_pre_com, do_post_com, settings) + "\n" - return script - - def do_single_trans_loop_each(self, exp, settings, do_pre_com, do_post_com): - script = "" - if settings.num_trans > 0 and exp.trans > 0: - script += self.set_trans_aperture(settings.a2_setting_trans) - script += self.do_single_run(True, exp, True, do_pre_com, do_post_com, settings) + "\n" - return script - - def do_all_sans(self, runs, settings, do_pre_com, do_post_com): - script = "" - if settings.num_sans > 0: - script += self.set_sans_aperture(settings.a2_setting_sans) - script += "IF sansCount < numSANS\n\n" - for exp in runs: - script += self.do_single_run(False, exp, False, do_pre_com, do_post_com, settings) + "\n" - script += "sansCount = sansCount + 1\n\n" - script += "ENDIF\n\n" - return script - - def do_all_trans(self, runs, settings, do_pre_com, do_post_com): - script = "" - if settings.num_trans > 0: - script += self.set_trans_aperture(settings.a2_setting_trans) - script += "IF transCount < numTRANS\n\n" - for exp in runs: - script += self.do_single_run(True, exp, False, do_pre_com, do_post_com, settings) + "\n" - script += "transCount = transCount + 1\n\n" - script += "ENDIF\n\n" - return script - - def do_all_sans_loop_each(self, runs, settings, do_pre_com, do_post_com): - script = "" - if settings.num_sans > 0: - script += self.set_sans_aperture(settings.a2_setting_sans) - for exp in runs: - script += self.do_single_run(False, exp, True, do_pre_com, do_post_com, settings) + "\n" - return script - - def do_all_trans_loop_each(self, runs, settings, do_pre_com, do_post_com): - script = "" - if settings.num_trans > 0: - script += self.set_trans_aperture(settings.a2_setting_trans) - for exp in runs: - script += self.do_single_run(True, exp, True, do_pre_com, do_post_com, settings) + "\n" - return script - - def set_sans_aperture(self, a2_size_sans): - script = "" - if a2_size_sans == "Large": - script += "DO_SANS /LARGE\n\n" - elif a2_size_sans == "Medium": - script += "DO_SANS /MEDIUM\n\n" - elif a2_size_sans == "Small": - script += "DO_SANS /SMALL\n\n" - elif a2_size_sans == "Trans": - script += "DO_SANS /TRANS\n\n" - return script - - def set_trans_aperture(self, a2_size_trans): - script = "" - if not a2_size_trans: - script += "DO_TRANS\n\n" - else: - if a2_size_trans == "Large": - script += "DO_TRANS /LARGE\n\n" - elif a2_size_trans == "Medium": - script += "DO_TRANS /MEDIUM\n\n" - elif a2_size_trans == "Small": - script += "DO_TRANS /SMALL\n\n" - elif a2_size_trans == "Trans": - script += "DO_TRANS /TRANS\n\n" - return script - - def do_single_run(self, is_trans, exp, is_loop, do_pre_com, do_post_com, settings): - script = "" - if exp.trans > 0 or exp.sans > 0: - if exp.rb_number: - script += f"change rbno={exp.rb_number}\n" - if exp.position: - if do_pre_com: - script += self.do_sample_environment(exp) - if is_loop: - if is_trans: - script += "LOOP transCount FROM 1 TO numTRANS\n" - else: - script += "LOOP sansCount FROM 1 TO numSANS\n" - script += self.create_move_command(is_trans, exp, settings) - script += "ENDLOOP\n" - else: - script += self.create_move_command(is_trans, exp, settings) - if do_post_com and exp.post_command: - script += exp.post_command + "\n" - return script - - def do_sample_environment(self, exp): - script = "" - if exp.temperature1: - script += f"CSET TEMP={exp.temperature1}\n" - if exp.temperature2: - script += f"CSET TEMP2={exp.temperature2}\n" - if exp.pre_command: - script += exp.pre_command + "\n" - if exp.field: - script += f"CSET FIELD={exp.field}\n" - if exp.shear_rate1: - script += f"CSET SHEAR_RATE_1={exp.shear_rate1}\n" - if exp.shear_rate2: - script += f"CSET SHEAR_RATE_2={exp.shear_rate2}\n" - if exp.shear_angle1: - script += f"CSET SHEAR_ANGLE_1={exp.shear_angle1}\n" - if exp.shear_angle2: - script += f"CSET SHEAR_ANGLE_2={exp.shear_angle2}\n" - return script - - def create_move_command(self, is_trans, exp, settings): - script = f"MOVE pos="{exp.position}"" - fulltitle = f"{{{exp.sample}}}" - if exp.background: - fulltitle += f"_{{{exp.background}}}" - if exp.thickness: - script += f" thick={exp.thickness}" - else: - script += " thick=1" - if exp.period: - script += f" period={exp.period}" - if is_trans: - script += self.write_wait_for(exp.trans_wait, exp.trans) - if fulltitle: - script += f" title="{fulltitle}_TRANS"" - else: - script += self.write_wait_for(exp.sans_wait, exp.sans) - if fulltitle: - if settings.collection_mode in ["TRANSSANS_Events", "TRANSSANS_Histogram"]: - script += f" title="{fulltitle}_TRANS_SANS"" - else: - script += f" title="{fulltitle}_SANS"" - return script + "\n" - - def write_wait_for(self, waitfor, length): - script = "" - if waitfor: - if waitfor.lower() == "MicroAmps".lower(): - script += f" uAhr={length}" - elif waitfor.lower() == "Minutes".lower(): - script += f" min={length}" - elif waitfor.lower() == "Seconds".lower(): - script += f" seconds={length}" - elif waitfor.lower() == "Frames".lower(): - script += f" frame={length}" - else: - script += f" uAhr={length}" - else: - script += f" uAhr={length}" - return script - - def estimate_run_time(self, runs, settings, estimation): - minutes = 0 - if settings.num_sans > 0: - minutes += self.calculate_time(runs, True, estimation.count_rate, estimation.move_time) * settings.num_sans - if settings.num_trans > 0: - minutes += self.calculate_time(runs, False, estimation.count_rate, estimation.move_time) * settings.num_trans - time = datetime(1, 1, 1, 0, 0, 0) + timedelta(minutes=minutes) - return time.strftime("%H:%M:%S") - - @staticmethod - def calculate_time(runs, is_sans, count_rate, seconds_between): - minutes = 0 - for exp in runs: - if exp.position: - if is_sans: - minutes += OpenGenieScriptGenerator.get_minutes(exp.sans_wait, exp.sans, count_rate) - else: - minutes += OpenGenieScriptGenerator.get_minutes(exp.trans_wait, exp.trans, count_rate) - minutes += seconds_between / 60 - return minutes - - @staticmethod - def get_minutes(wait, length, count_rate): - if wait == "Minutes": - return length - elif wait == "Seconds": - return length / 60.0 - elif wait == "Frames": - return length / (25.0 * 60.0) - else: - return length / (count_rate / 60.0) - -class Experiment: - def __init__(self, position="", trans=0, sans=0, trans_wait="", sans_wait="", sample="", background="", thickness=0, pre_command="", post_command="", temperature1="", temperature2="", field="", shear_rate1="", shear_rate2="", shear_angle1="", shear_angle2="", rb_number="", period=""): - self.position = position - self.trans = trans - self.sans = sans - self.trans_wait = trans_wait - self.sans_wait = sans_wait - self.sample = sample - self.background = background - self.thickness = thickness - self.pre_command = pre_command - self.post_command = post_command - self.temperature1 = temperature1 - self.temperature2 = temperature2 - self.field = field - self.shear_rate1 = shear_rate1 - self.shear_rate2 = shear_rate2 - self.shear_angle1 = shear_angle1 - self.shear_angle2 = shear_angle2 - self.rb_number = rb_number - self.period = period - -class ExperimentSettings: - def __init__(self, sample_width="", sample_height="", sample_geometry="", order="", num_trans=0, num_sans=0, collection_mode="", a2_setting_trans="", a2_setting_sans="", loop_per_run=False): - self.sample_width = sample_width - self.sample_height = sample_height - self.sample_geometry = sample_geometry - self.order = order - self.num_trans = num_trans - self.num_sans = num_sans - self.collection_mode = collection_mode - self.a2_setting_trans = a2_setting_trans - self.a2_setting_sans = a2_setting_sans - self.loop_per_run = loop_per_run - -class Estimation: - def __init__(self, count_rate=0, move_time=0): - self.count_rate = count_rate - self.move_time = move_time - -if __name__ == "__main__": - runs = [Experiment(position="pos1", trans=10, sans=20, trans_wait="Minutes", sans_wait="Minutes", sample="Sample1", thickness=1.0)] - settings = ExperimentSettings(sample_width="10", sample_height="10", sample_geometry="Flat", order="AllTrans", num_trans=1, num_sans=1, collection_mode="Events", a2_setting_trans="Large", a2_setting_sans="Large", loop_per_run=False) - estimation = Estimation(count_rate=100, move_time=10) - generator = OpenGenieScriptGenerator(is_sans2d=True) - script = generator.generate_script(runs, settings, "my_script") - print(script) - estimated_time = generator.estimate_run_time(runs, settings, estimation) - print(f"Estimated run time: {estimated_time}") -``` - -In this Python version: -- The `OpenGenieScriptGenerator` class is implemented with methods to generate the script and estimate run time. -- The `Experiment`, `ExperimentSettings`, and `Estimation` classes are implemented as placeholders for the actual data structures. -- The `if __name__ == "__main__":` block demonstrates how to use the `OpenGenieScriptGenerator` class to generate a script and estimate run time. - -dehoni: Please translate the selected code into python and use approbriate packages. - -GitHub Copilot: Here is the translated code into Python using the `setuptools` package for setting up package metadata: - -```python - - -from setuptools import setup, find_packages - -setup( - name="LOQ_Script_Gui", - version="1.0.0", - description="", - author="", - author_email="", - url="", - packages=find_packages(), - include_package_data=True, - install_requires=[ - # Add your dependencies here - ], - classifiers=[ - "Programming Language :: Python :: 3", - "License :: OSI Approved :: MIT License", - "Operating System :: OS Independent", - ], - python_requires=">=3.6", -) - -# Additional metadata for localization and resource management can be added as needed. -``` - -In this Python version: -- The `setup.py` file is used to define the package metadata and dependencies. -- The `setuptools` package is used to handle the package setup. -- The `find_packages` function is used to automatically discover all packages and subpackages. -- The `install_requires` list can be populated with the required dependencies. -- The `classifiers` list is used to provide additional metadata about the package. - -dehoni: Please translate the selected code into python and use approbriate packages. - -GitHub Copilot: Here is the translated code into Python using the `gettext` package for handling localization and resource management: - -```python - - -import gettext -import os - -class Resources: - def __init__(self): - self.resource_man = None - self.resource_culture = None - - @property - def resource_manager(self): - if self.resource_man is None: - locale_dir = os.path.join(os.path.dirname(__file__), "locale") - self.resource_man = gettext.translation("resources", localedir=locale_dir, languages=[self.resource_culture]) - return self.resource_man - - @property - def culture(self): - return self.resource_culture - - @culture.setter - def culture(self, value): - self.resource_culture = value - self.resource_man = None # Reset the resource manager to reload with the new culture - -# Example usage -resources = Resources() -resources.culture = "en_US" # Set the desired culture -_ = resources.resource_manager.gettext - -# Example of using the localized string -print(_("Hello, World!")) -``` - -In this Python version: -- The `gettext` package is used for localization and resource management. -- The - -Resources - - class manages the resource manager and culture settings. -- The `resource_manager` property initializes the `gettext.translation` object based on the specified culture. -- The `culture` property allows setting the desired culture and resets the resource manager to reload with the new culture. -- An example usage is provided to demonstrate how to set the culture and use the localized strings. - -dehoni: Please translate the selected code into python and use approbriate packages. - -GitHub Copilot: Here is the translated code into Python using the `configparser` package for handling resource files: - -```python - - -[resheader] -resmimetype = text/microsoft-resx -version = 2.0 -reader = System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 -writer = System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - -[data] -Name1 = this is my long string -Color1 = Blue -Bitmap1 = [base64 mime encoded serialized .NET Framework object] -Icon1 = [base64 mime encoded string representing a byte array form of the .NET Framework object] - -[metadata] -Name1.comment = this is a comment -Icon1.comment = This is a comment -``` - -In this Python version: -- The `.resx` file is translated into an `.ini` file format using the `configparser` package. -- The `resheader` section contains metadata about the resource file. -- The `data` section contains the actual resource data. -- The `metadata` section contains comments associated with the data. - -To read this `.ini` file in Python, you can use the following code: - -```python - - -import configparser - -config = configparser.ConfigParser() -config.read("resources.ini") - -# Accessing resheader values -resmimetype = config["resheader"]["resmimetype"] -version = config["resheader"]["version"] -reader = config["resheader"]["reader"] -writer = config["resheader"]["writer"] - -# Accessing data values -name1 = config["data"]["Name1"] -color1 = config["data"]["Color1"] -bitmap1 = config["data"]["Bitmap1"] -icon1 = config["data"]["Icon1"] - -# Accessing metadata comments -name1_comment = config["metadata"]["Name1.comment"] -icon1_comment = config["metadata"]["Icon1.comment"] - -print(f"Name1: {name1} (Comment: {name1_comment})") -print(f"Color1: {color1}") -print(f"Bitmap1: {bitmap1}") -print(f"Icon1: {icon1} (Comment: {icon1_comment})") -``` - -This code reads the `resources.ini` file and prints the values and comments for the resources. - -dehoni: Please translate the selected code into python and use approbriate packages. - -GitHub Copilot: Here is the translated code into Python using the `configparser` package for handling application settings: - -```python - - -[DEFAULT] -LastInstrument = LARMOR -SampleHeight = 11 -SampleWidth = 11 -Geometry = Flat Plate -RunOrder = Alternate - TRANS first -NumTrans = 1 -NumSans = 1 -LoopPerRun = False -A2SettingSans = Large = 25, 16 mm -A2SettingTrans = Large = 25, 16 mm -CollectionMode = Events -``` - -To read and write these settings in Python, you can use the following code: - -```python - - -import configparser - -class Settings: - def __init__(self, filename="settings.ini"): - self.config = configparser.ConfigParser() - self.filename = filename - self.config.read(self.filename) - - @property - def last_instrument(self): - return self.config["DEFAULT"]["LastInstrument"] - - @last_instrument.setter - def last_instrument(self, value): - self.config["DEFAULT"]["LastInstrument"] = value - self.save() - - @property - def sample_height(self): - return self.config["DEFAULT"]["SampleHeight"] - - @sample_height.setter - def sample_height(self, value): - self.config["DEFAULT"]["SampleHeight"] = value - self.save() - - @property - def sample_width(self): - return self.config["DEFAULT"]["SampleWidth"] - - @sample_width.setter - def sample_width(self, value): - self.config["DEFAULT"]["SampleWidth"] = value - self.save() - - @property - def geometry(self): - return self.config["DEFAULT"]["Geometry"] - - @geometry.setter - def geometry(self, value): - self.config["DEFAULT"]["Geometry"] = value - self.save() - - @property - def run_order(self): - return self.config["DEFAULT"]["RunOrder"] - - @run_order.setter - def run_order(self, value): - self.config["DEFAULT"]["RunOrder"] = value - self.save() - - @property - def num_trans(self): - return self.config["DEFAULT"]["NumTrans"] - - @num_trans.setter - def num_trans(self, value): - self.config["DEFAULT"]["NumTrans"] = value - self.save() - - @property - def num_sans(self): - return self.config["DEFAULT"]["NumSans"] - - @num_sans.setter - def num_sans(self, value): - self.config["DEFAULT"]["NumSans"] = value - self.save() - - @property - def loop_per_run(self): - return self.config["DEFAULT"]["LoopPerRun"] - - @loop_per_run.setter - def loop_per_run(self, value): - self.config["DEFAULT"]["LoopPerRun"] = value - self.save() - - @property - def a2_setting_sans(self): - return self.config["DEFAULT"]["A2SettingSans"] - - @a2_setting_sans.setter - def a2_setting_sans(self, value): - self.config["DEFAULT"]["A2SettingSans"] = value - self.save() - - @property - def a2_setting_trans(self): - return self.config["DEFAULT"]["A2SettingTrans"] - - @a2_setting_trans.setter - def a2_setting_trans(self, value): - self.config["DEFAULT"]["A2SettingTrans"] = value - self.save() - - @property - def collection_mode(self): - return self.config["DEFAULT"]["CollectionMode"] - - @collection_mode.setter - def collection_mode(self, value): - self.config["DEFAULT"]["CollectionMode"] = value - self.save() - - def save(self): - with open(self.filename, "w") as configfile: - self.config.write(configfile) - -# Example usage -settings = Settings() -print(settings.last_instrument) -settings.last_instrument = "LOQ" -print(settings.last_instrument) -``` - -In this Python version: -- The `settings.ini` file is used to store the application settings. -- The - -Settings - - class provides properties to get and set the settings values. -- The `save` method writes the updated settings back to the `settings.ini` file. -- An example usage is provided to demonstrate how to read and update the settings. - -dehoni: Please translate the selected code into python and use approbriate packages. - -GitHub Copilot: Here is the translated code into Python using the `configparser` package for handling application settings: - -```python - - -[DEFAULT] -LastInstrument = LARMOR -SampleHeight = 11 -SampleWidth = 11 -Geometry = Flat Plate -RunOrder = Alternate - TRANS first -NumTrans = 1 -NumSans = 1 -LoopPerRun = False -A2SettingSans = Large = 25, 16 mm -A2SettingTrans = Large = 25, 16 mm -CollectionMode = Events -``` - -To read and write these settings in Python, you can use the following code: - -```python - - -import configparser - -class Settings: - def __init__(self, filename="settings.ini"): - self.config = configparser.ConfigParser() - self.filename = filename - self.config.read(self.filename) - - @property - def last_instrument(self): - return self.config["DEFAULT"]["LastInstrument"] - - @last_instrument.setter - def last_instrument(self, value): - self.config["DEFAULT"]["LastInstrument"] = value - self.save() - - @property - def sample_height(self): - return self.config["DEFAULT"]["SampleHeight"] - - @sample_height.setter - def sample_height(self, value): - self.config["DEFAULT"]["SampleHeight"] = value - self.save() - - @property - def sample_width(self): - return self.config["DEFAULT"]["SampleWidth"] - - @sample_width.setter - def sample_width(self, value): - self.config["DEFAULT"]["SampleWidth"] = value - self.save() - - @property - def geometry(self): - return self.config["DEFAULT"]["Geometry"] - - @geometry.setter - def geometry(self, value): - self.config["DEFAULT"]["Geometry"] = value - self.save() - - @property - def run_order(self): - return self.config["DEFAULT"]["RunOrder"] - - @run_order.setter - def run_order(self, value): - self.config["DEFAULT"]["RunOrder"] = value - self.save() - - @property - def num_trans(self): - return self.config["DEFAULT"]["NumTrans"] - - @num_trans.setter - def num_trans(self, value): - self.config["DEFAULT"]["NumTrans"] = value - self.save() - - @property - def num_sans(self): - return self.config["DEFAULT"]["NumSans"] - - @num_sans.setter - def num_sans(self, value): - self.config["DEFAULT"]["NumSans"] = value - self.save() - - @property - def loop_per_run(self): - return self.config["DEFAULT"]["LoopPerRun"] - - @loop_per_run.setter - def loop_per_run(self, value): - self.config["DEFAULT"]["LoopPerRun"] = value - self.save() - - @property - def a2_setting_sans(self): - return self.config["DEFAULT"]["A2SettingSans"] - - @a2_setting_sans.setter - def a2_setting_sans(self, value): - self.config["DEFAULT"]["A2SettingSans"] = value - self.save() - - @property - def a2_setting_trans(self): - return self.config["DEFAULT"]["A2SettingTrans"] - - @a2_setting_trans.setter - def a2_setting_trans(self, value): - self.config["DEFAULT"]["A2SettingTrans"] = value - self.save() - - @property - def collection_mode(self): - return self.config["DEFAULT"]["CollectionMode"] - - @collection_mode.setter - def collection_mode(self, value): - self.config["DEFAULT"]["CollectionMode"] = value - self.save() - - def save(self): - with open(self.filename, "w") as configfile: - self.config.write(configfile) - -# Example usage -settings = Settings() -print(settings.last_instrument) -settings.last_instrument = "LOQ" -print(settings.last_instrument) -``` - -In this Python version: -- The `settings.ini` file is used to store the application settings. -- The `Settings` class provides properties to get and set the settings values. -- The `save` method writes the updated settings back to the `settings.ini` file. -- An example usage is provided to demonstrate how to read and update the settings. - -dehoni: Please translate the selected code into python and use approbriate packages. - -GitHub Copilot: Here is the translated code into Python: - -```python - - -class A2Setting: - X_LARGE = "XLarge = 20x18 mm" - LARGE = "Large = 17 mm" - MEDIUM = "Medium = 12 mm" - SMALL = "Small = 5/1 mm" - TRANS = "Trans = 1 mm" - LARMOR = "" - # LARMOR_LARGE = "Large = 25, 16 mm" - # LARMOR_MEDIUM = "Medium = 20, 14 mm" - # LARMOR_SMALL = "Small = 15, 10 mm" - -# Example usage -if __name__ == "__main__": - print(A2Setting.X_LARGE) - print(A2Setting.LARGE) - print(A2Setting.MEDIUM) - print(A2Setting.SMALL) - print(A2Setting.TRANS) - print(A2Setting.LARMOR) -``` - -In this Python version: -- The - -A2Setting - - class is defined with class attributes for each setting. -- The commented-out settings from the original C# code are also commented out in the Python version. -- An example usage is provided to demonstrate how to access the settings. - -dehoni: Please translate the selected code into python and use approbriate packages. - -GitHub Copilot: Here is the translated code into Python: - -```python - - -class DataCollection: - HISTOGRAM = "Histogram" - EVENTS = "Events" - SESANS = "SESANS" - TRANSSANS_EVENTS = "TRANSSANS_Events" - TRANSSANS_HISTOGRAM = "TRANSSANS_Histogram" - - choices = [ - HISTOGRAM, - TRANSSANS_HISTOGRAM, - EVENTS, - TRANSSANS_EVENTS, - SESANS - ] - - @staticmethod - def get_choices(): - return DataCollection.choices - -# Example usage -if __name__ == "__main__": - print(DataCollection.get_choices()) -``` - -In this Python version: -- The - -DataCollection - - class is defined with class attributes for each data collection type. -- The - -choices - - list contains all the data collection types. -- The `get_choices` static method returns the list of choices. -- An example usage is provided to demonstrate how to access the choices. - -dehoni: Please translate the selected code into python and use approbriate packages. - -GitHub Copilot: Here is the translated code into Python using the `tkinter` package for data binding and validation: - -```python - - -import tkinter as tk - -class Estimation: - def __init__(self): - self._count_rate = tk.DoubleVar(value=40) - self._move_time = tk.DoubleVar(value=120) - self._script_time = tk.StringVar() - self._is_valid = tk.BooleanVar(value=True) - self.property_changed = [] - - self._count_rate.trace_add("write", self.validate) - self._move_time.trace_add("write", self.validate) - - @property - def count_rate(self): - return self._count_rate.get() - - @count_rate.setter - def count_rate(self, value): - self._count_rate.set(value) - self.on_property_changed("count_rate") - - @property - def move_time(self): - return self._move_time.get() - - @move_time.setter - def move_time(self, value): - self._move_time.set(value) - self.on_property_changed("move_time") - - @property - def script_time(self): - return self._script_time.get() - - @script_time.setter - def script_time(self, value): - self._script_time.set(value) - self.on_property_changed("script_time") - - @property - def is_valid(self): - return self._is_valid.get() - - @is_valid.setter - def is_valid(self, value): - self._is_valid.set(value) - self.on_property_changed("is_valid") - - def on_property_changed(self, name): - for callback in self.property_changed: - callback(name) - - def validate(self, *args): - error_message = "" - if self.count_rate < 0: - error_message = "Count rate cannot be negative!" - elif self.move_time < 0: - error_message = "Move time cannot be negative!" - - if error_message: - self.is_valid = False - else: - self.is_valid = True - - return error_message - -# Example usage -if __name__ == "__main__": - def on_property_changed(name): - print(f"Property {name} changed") - - estimation = Estimation() - estimation.property_changed.append(on_property_changed) - - print(estimation.count_rate) - estimation.count_rate = -10 # This should trigger validation and set is_valid to False - print(estimation.is_valid) - estimation.count_rate = 50 # This should trigger validation and set is_valid to True - print(estimation.is_valid) -``` - -In this Python version: -- The - -Estimation - - class uses `tkinter` variable classes (`DoubleVar`, `StringVar`, `BooleanVar`) for data binding. -- The `validate` method checks the validity of `count_rate` and `move_time` and updates the `is_valid` property accordingly. -- The `on_property_changed` method notifies subscribers when a property changes. -- An example usage is provided to demonstrate how to use the - -Estimation - - class and handle property changes. - -dehoni: Please translate the selected code into python and use approbriate packages. - -GitHub Copilot: Here is the translated code into Python using the `tkinter` package for data binding and property change notifications: - -```python - - -import tkinter as tk - -class Experiment: - def __init__(self): - self._position = tk.StringVar() - self._trans = tk.DoubleVar(value=0) - self._trans_wait = tk.StringVar() - self._sans = tk.DoubleVar(value=0) - self._sans_wait = tk.StringVar() - self._period = tk.StringVar() - self._background = tk.StringVar() - self._sample = tk.StringVar() - self._thickness = tk.StringVar() - self._temperature1 = tk.StringVar() - self._temperature2 = tk.StringVar() - self._field = tk.StringVar() - self._shear_rate1 = tk.StringVar() - self._shear_rate2 = tk.StringVar() - self._shear_angle1 = tk.StringVar() - self._shear_angle2 = tk.StringVar() - self._pre_command = tk.StringVar() - self._post_command = tk.StringVar() - self._rb_number = tk.StringVar() - self.property_changed = [] - - self._position.trace_add("write", self.on_property_changed) - self._trans.trace_add("write", self.on_property_changed) - self._trans_wait.trace_add("write", self.on_property_changed) - self._sans.trace_add("write", self.on_property_changed) - self._sans_wait.trace_add("write", self.on_property_changed) - self._period.trace_add("write", self.on_property_changed) - self._background.trace_add("write", self.on_property_changed) - self._sample.trace_add("write", self.on_property_changed) - self._thickness.trace_add("write", self.on_property_changed) - self._temperature1.trace_add("write", self.on_property_changed) - self._temperature2.trace_add("write", self.on_property_changed) - self._field.trace_add("write", self.on_property_changed) - self._shear_rate1.trace_add("write", self.on_property_changed) - self._shear_rate2.trace_add("write", self.on_property_changed) - self._shear_angle1.trace_add("write", self.on_property_changed) - self._shear_angle2.trace_add("write", self.on_property_changed) - self._pre_command.trace_add("write", self.on_property_changed) - self._post_command.trace_add("write", self.on_property_changed) - self._rb_number.trace_add("write", self.on_property_changed) - - def on_property_changed(self, *args): - for callback in self.property_changed: - callback(args[0]) - - @property - def position(self): - return self._position.get() - - @position.setter - def position(self, value): - self._position.set(value) - - @property - def trans(self): - return self._trans.get() - - @trans.setter - def trans(self, value): - self._trans.set(value) - - @property - def trans_wait(self): - return self._trans_wait.get() - - @trans_wait.setter - def trans_wait(self, value): - self._trans_wait.set(value) - - @property - def sans(self): - return self._sans.get() - - @sans.setter - def sans(self, value): - self._sans.set(value) - - @property - def sans_wait(self): - return self._sans_wait.get() - - @sans_wait.setter - def sans_wait(self, value): - self._sans_wait.set(value) - - @property - def period(self): - return self._period.get() - - @period.setter - def period(self, value): - self._period.set(value) - - @property - def background(self): - return self._background.get() - - @background.setter - def background(self, value): - self._background.set(value) - - @property - def sample(self): - return self._sample.get() - - @sample.setter - def sample(self, value): - self._sample.set(value) - - @property - def thickness(self): - return self._thickness.get() - - @thickness.setter - def thickness(self, value): - self._thickness.set(value) - - @property - def temperature1(self): - return self._temperature1.get() - - @temperature1.setter - def temperature1(self, value): - self._temperature1.set(value) - - @property - def temperature2(self): - return self._temperature2.get() - - @temperature2.setter - def temperature2(self, value): - self._temperature2.set(value) - - @property - def field(self): - return self._field.get() - - @field.setter - def field(self, value): - self._field.set(value) - - @property - def shear_rate1(self): - return self._shear_rate1.get() - - @shear_rate1.setter - def shear_rate1(self, value): - self._shear_rate1.set(value) - - @property - def shear_rate2(self): - return self._shear_rate2.get() - - @shear_rate2.setter - def shear_rate2(self, value): - self._shear_rate2.set(value) - - @property - def shear_angle1(self): - return self._shear_angle1.get() - - @shear_angle1.setter - def shear_angle1(self, value): - self._shear_angle1.set(value) - - @property - def shear_angle2(self): - return self._shear_angle2.get() - - @shear_angle2.setter - def shear_angle2(self, value): - self._shear_angle2.set(value) - - @property - def pre_command(self): - return self._pre_command.get() - - @pre_command.setter - def pre_command(self, value): - self._pre_command.set(value) - - @property - def post_command(self): - return self._post_command.get() - - @post_command.setter - def post_command(self, value): - self._post_command.set(value) - - @property - def rb_number(self): - return self._rb_number.get() - - @rb_number.setter - def rb_number(self, value): - self._rb_number.set(value) - -# Example usage -if __name__ == "__main__": - def on_property_changed(name): - print(f"Property {name} changed") - - experiment = Experiment() - experiment.property_changed.append(on_property_changed) - - experiment.position = "New Position" - experiment.trans = 10.5 - experiment.sans = 20.0 - experiment.temperature1 = "25" - experiment.temperature2 = "30" - experiment.field = "1.5T" - experiment.shear_rate1 = "100" - experiment.shear_rate2 = "200" - experiment.shear_angle1 = "45" - experiment.shear_angle2 = "90" - experiment.pre_command = "Pre Command" - experiment.post_command = "Post Command" - experiment.rb_number = "RB12345" -``` - -In this Python version: -- The - -Experiment - - class uses `tkinter` variable classes (`StringVar`, `DoubleVar`) for data binding. -- The `on_property_changed` method notifies subscribers when a property changes. -- Properties are defined for each attribute to get and set the values. -- An example usage is provided to demonstrate how to use the - -Experiment - - class and handle property changes. - -dehoni: Please translate the selected code into python and use approbriate packages. - -GitHub Copilot: Here is the translated code into Python using the `tkinter` package for data binding and property change notifications: - -```python - - -import tkinter as tk -from configparser import ConfigParser - -class ExperimentSettings: - def __init__(self, config_file="settings.ini"): - self.config = ConfigParser() - self.config.read(config_file) - self.config_file = config_file - - self._order = tk.StringVar(value=self.config["DEFAULT"].get("RunOrder", "TransFirst")) - self._num_sans = tk.IntVar(value=self.config["DEFAULT"].getint("NumSans", 1)) - self._num_trans = tk.IntVar(value=self.config["DEFAULT"].getint("NumTrans", 1)) - self._loop_per_run = tk.BooleanVar(value=self.config["DEFAULT"].getboolean("LoopPerRun", False)) - self._a2_setting_sans = tk.StringVar(value=self.config["DEFAULT"].get("A2SettingSans", "Large")) - self._a2_setting_trans = tk.StringVar(value=self.config["DEFAULT"].get("A2SettingTrans", "Large")) - self._sample_geometry = tk.StringVar(value=self.config["DEFAULT"].get("Geometry", "Flat Plate")) - self._sample_height = tk.DoubleVar(value=self.config["DEFAULT"].getfloat("SampleHeight", 11)) - self._sample_width = tk.DoubleVar(value=self.config["DEFAULT"].getfloat("SampleWidth", 11)) - self._sample_width_enabled = tk.BooleanVar(value=True) - self._collection_mode = tk.StringVar(value=self.config["DEFAULT"].get("CollectionMode", "Events")) - self._is_valid = tk.BooleanVar(value=True) - self.property_changed = [] - - self._order.trace_add("write", self.on_property_changed) - self._num_sans.trace_add("write", self.on_property_changed) - self._num_trans.trace_add("write", self.on_property_changed) - self._loop_per_run.trace_add("write", self.on_property_changed) - self._a2_setting_sans.trace_add("write", self.on_property_changed) - self._a2_setting_trans.trace_add("write", self.on_property_changed) - self._sample_geometry.trace_add("write", self.on_property_changed) - self._sample_height.trace_add("write", self.on_property_changed) - self._sample_width.trace_add("write", self.on_property_changed) - self._collection_mode.trace_add("write", self.on_property_changed) - - def on_property_changed(self, *args): - for callback in self.property_changed: - callback(args[0]) - self.save() - - def save(self): - self.config["DEFAULT"]["RunOrder"] = self._order.get() - self.config["DEFAULT"]["NumSans"] = str(self._num_sans.get()) - self.config["DEFAULT"]["NumTrans"] = str(self._num_trans.get()) - self.config["DEFAULT"]["LoopPerRun"] = str(self._loop_per_run.get()) - self.config["DEFAULT"]["A2SettingSans"] = self._a2_setting_sans.get() - self.config["DEFAULT"]["A2SettingTrans"] = self._a2_setting_trans.get() - self.config["DEFAULT"]["Geometry"] = self._sample_geometry.get() - self.config["DEFAULT"]["SampleHeight"] = str(self._sample_height.get()) - self.config["DEFAULT"]["SampleWidth"] = str(self._sample_width.get()) - self.config["DEFAULT"]["CollectionMode"] = self._collection_mode.get() - with open(self.config_file, "w") as configfile: - self.config.write(configfile) - - @property - def order(self): - return self._order.get() - - @order.setter - def order(self, value): - self._order.set(value) - - @property - def num_sans(self): - return self._num_sans.get() - - @num_sans.setter - def num_sans(self, value): - self._num_sans.set(value) - - @property - def num_trans(self): - return self._num_trans.get() - - @num_trans.setter - def num_trans(self, value): - self._num_trans.set(value) - - @property - def loop_per_run(self): - return self._loop_per_run.get() - - @loop_per_run.setter - def loop_per_run(self, value): - self._loop_per_run.set(value) - - @property - def a2_setting_sans(self): - return self._a2_setting_sans.get() - - @a2_setting_sans.setter - def a2_setting_sans(self, value): - self._a2_setting_sans.set(value) - - @property - def a2_setting_trans(self): - return self._a2_setting_trans.get() - - @a2_setting_trans.setter - def a2_setting_trans(self, value): - self._a2_setting_trans.set(value) - - @property - def sample_geometry(self): - return self._sample_geometry.get() - - @sample_geometry.setter - def sample_geometry(self, value): - self._sample_geometry.set(value) - self.sample_width_enabled = value != "Disc" - - @property - def sample_height(self): - return self._sample_height.get() - - @sample_height.setter - def sample_height(self, value): - self._sample_height.set(value) - if not self.sample_width_enabled: - self.sample_width = value - - @property - def sample_width(self): - return self._sample_width.get() - - @sample_width.setter - def sample_width(self, value): - self._sample_width.set(value) - - @property - def sample_width_enabled(self): - return self._sample_width_enabled.get() - - @sample_width_enabled.setter - def sample_width_enabled(self, value): - self._sample_width_enabled.set(value) - if not value: - self.sample_width = self.sample_height - - @property - def collection_mode(self): - return self._collection_mode.get() - - @collection_mode.setter - def collection_mode(self, value): - self._collection_mode.set(value) - - @property - def is_valid(self): - return self._is_valid.get() - - @is_valid.setter - def is_valid(self, value): - self._is_valid.set(value) - - def validate(self): - error_message = "" - if self.num_sans < 0: - error_message = "Number of SANS runs cannot be negative!" - elif self.num_trans < 0: - error_message = "Number of TRANS runs cannot be negative!" - elif self.sample_height <= 0 or self.sample_height > 20: - error_message = "Sample height must be between 0 and 20" - elif self.sample_width <= 0 or self.sample_width > 20: - error_message = "Sample width must be between 0 and 20" - - if error_message: - self.is_valid = False - else: - self.is_valid = True - - return error_message - -# Example usage -if __name__ == "__main__": - def on_property_changed(name): - print(f"Property {name} changed") - - settings = ExperimentSettings() - settings.property_changed.append(on_property_changed) - - settings.order = "SansFirst" - settings.num_sans = 2 - settings.num_trans = 3 - settings.sample_height = 15 - settings.sample_width = 10 - settings.collection_mode = "Histogram" - print(settings.validate()) -``` - -In this Python version: -- The - -ExperimentSettings - - class uses `tkinter` variable classes (`StringVar`, `IntVar`, `DoubleVar`, `BooleanVar`) for data binding. -- The `on_property_changed` method notifies subscribers when a property changes and saves the settings to the `settings.ini` file. -- Properties are defined for each attribute to get and set the values. -- The `validate` method checks the validity of the settings and updates the `is_valid` property accordingly. -- An example usage is provided to demonstrate how to use the - -ExperimentSettings - - class and handle property changes. - -dehoni: Please translate the selected code into python and use approbriate packages. - -GitHub Copilot: Here is the translated code into Python: - -```python - - -class Geometry: - DISC = "Disc" - CYLINDRICAL = "Cylindrical" - FLAT_PLATE = "Flat Plate" - SINGLE_CRYSTAL = "Single Crystal" - - choices = [ - DISC, - CYLINDRICAL, - FLAT_PLATE, - SINGLE_CRYSTAL - ] - - @staticmethod - def get_choices(): - return Geometry.choices - -# Example usage -if __name__ == "__main__": - print(Geometry.get_choices()) -``` - -In this Python version: -- The - -Geometry - - class is defined with class attributes for each geometry type. -- The - -choices - - list contains all the geometry types. -- The `get_choices` static method returns the list of choices. -- An example usage is provided to demonstrate how to access the choices. - -dehoni: Please translate the selected code into python and use approbriate packages. - -GitHub Copilot: Here is the translated code into Python using the `tkinter` package for data binding and property change notifications: - -```python - - -import tkinter as tk -from tkinter import StringVar, BooleanVar -from typing import List - -class InstrumentInfo: - property_changed = [] - static_property_changed = [] - - def __init__(self): - self._a2_label = StringVar(value="A2 Setting:") - self._a2_choices = tk.Variable(value=["Large", "Medium", "Small"]) - self._a2_trans_enabled = BooleanVar(value=True) - self._collection_mode_enabled = BooleanVar(value=False) - self._script_generator = None - - self._a2_label.trace_add("write", self.on_property_changed) - self._a2_choices.trace_add("write", self.on_property_changed) - self._a2_trans_enabled.trace_add("write", self.on_property_changed) - self._collection_mode_enabled.trace_add("write", self.on_property_changed) - - def on_property_changed(self, *args): - for callback in self.property_changed: - callback(args[0]) - - @staticmethod - def on_static_property_changed(property_name): - for callback in InstrumentInfo.static_property_changed: - callback(property_name) - - @property - def a2_label(self): - return self._a2_label.get() - - @a2_label.setter - def a2_label(self, value): - self._a2_label.set(value) - self.on_static_property_changed("a2_label") - - @property - def a2_choices(self): - return self._a2_choices.get() - - @a2_choices.setter - def a2_choices(self, value: List[str]): - self._a2_choices.set(value) - self.on_static_property_changed("a2_choices") - - @property - def a2_trans_enabled(self): - return self._a2_trans_enabled.get() - - @a2_trans_enabled.setter - def a2_trans_enabled(self, value): - self._a2_trans_enabled.set(value) - self.on_static_property_changed("a2_trans_enabled") - - @property - def collection_mode_enabled(self): - return self._collection_mode_enabled.get() - - @collection_mode_enabled.setter - def collection_mode_enabled(self, value): - self._collection_mode_enabled.set(value) - self.on_static_property_changed("collection_mode_enabled") - - @property - def script_generator(self): - return self._script_generator - - @script_generator.setter - def script_generator(self, value): - self._script_generator = value - -# Example usage -if __name__ == "__main__": - def on_property_changed(name): - print(f"Property {name} changed") - - def on_static_property_changed(name): - print(f"Static property {name} changed") - - instrument_info = InstrumentInfo() - instrument_info.property_changed.append(on_property_changed) - InstrumentInfo.static_property_changed.append(on_static_property_changed) - - instrument_info.a2_label = "New A2 Setting:" - instrument_info.a2_choices = ["XLarge", "Large", "Medium", "Small"] - instrument_info.a2_trans_enabled = False - instrument_info.collection_mode_enabled = True -``` - -In this Python version: -- The - -InstrumentInfo - - class uses `tkinter` variable classes (`StringVar`, `BooleanVar`) for data binding. -- The `on_property_changed` method notifies subscribers when a property changes. -- The `on_static_property_changed` method notifies subscribers when a static property changes. -- Properties are defined for each attribute to get and set the values. -- An example usage is provided to demonstrate how to use the - -InstrumentInfo - - class and handle property changes. - -dehoni: Please translate the selected code into python and use approbriate packages. - -GitHub Copilot: Here is the translated code into Python: - -```python - - -class RunOrder: - ALL_TRANS = "All TRANS first" - TRANS_FIRST = "Alternate - TRANS first" - ALL_SANS = "All SANS first" - SANS_FIRST = "Alternate - SANS first" - - choices = [ - ALL_TRANS, - TRANS_FIRST, - ALL_SANS, - SANS_FIRST - ] - - @staticmethod - def get_choices(): - return RunOrder.choices - -# Example usage -if __name__ == "__main__": - print(RunOrder.get_choices()) -``` - -In this Python version: -- The - -RunOrder - - class is defined with class attributes for each run order type. -- The - -choices - - list contains all the run order types. -- The `get_choices` static method returns the list of choices. -- An example usage is provided to demonstrate how to access the choices. - -dehoni: Please translate the selected code into python and use approbriate packages. - -GitHub Copilot: Here is the translated code into Python: - -```python - - -class WaitForUnits: - MICROAMPS = "Microamp-hours" - MINUTES = "Minutes" - SECONDS = "Seconds" - FRAMES = "Frames" - - choices = [ - MICROAMPS, - MINUTES, - SECONDS, - FRAMES - ] - - @staticmethod - def get_choices(): - return WaitForUnits.choices - -# Example usage -if __name__ == "__main__": - print(WaitForUnits.get_choices()) -``` - -In this Python version: -- The - -WaitForUnits - - class is defined with class attributes for each wait unit type. -- The - -choices - - list contains all the wait unit types. -- The `get_choices` static method returns the list of choices. -- An example usage is provided to demonstrate how to access the choices. - -dehoni: Please translate the selected code into python and use approbriate packages. - -GitHub Copilot: Here is the translated code into Python: - -```python - - -import csv - -class CsvExporter: - @staticmethod - def export(file, runs): - with open(file, mode="w", newline="") as csvfile: - writer = csv.writer(csvfile) - for exp in runs: - row = [ - exp.position, - str(exp.trans), - exp.trans_wait, - str(exp.sans), - exp.sans_wait, - exp.period, - exp.sample, - exp.background, - exp.thickness, - exp.temperature1, - exp.temperature2, - exp.field, - exp.shear_rate1, - exp.shear_rate2, - exp.shear_angle1, - exp.shear_angle2, - exp.pre_command, - exp.post_command, - exp.rb_number - ] - writer.writerow(row) - -# Example usage -class Experiment: - def __init__(self, position="", trans=0, trans_wait="", sans=0, sans_wait="", period="", sample="", background="", thickness="", temperature1="", temperature2="", field="", shear_rate1="", shear_rate2="", shear_angle1="", shear_angle2="", pre_command="", post_command="", rb_number=""): - self.position = position - self.trans = trans - self.trans_wait = trans_wait - self.sans = sans - self.sans_wait = sans_wait - self.period = period - self.sample = sample - self.background = background - self.thickness = thickness - self.temperature1 = temperature1 - self.temperature2 = temperature2 - self.field = field - self.shear_rate1 = shear_rate1 - self.shear_rate2 = shear_rate2 - self.shear_angle1 = shear_angle1 - self.shear_angle2 = shear_angle2 - self.pre_command = pre_command - self.post_command = post_command - self.rb_number = rb_number - -if __name__ == "__main__": - runs = [ - Experiment(position="pos1", trans=10, trans_wait="Minutes", sans=20, sans_wait="Minutes", period="1", sample="Sample1", background="Background1", thickness="1.0", temperature1="25", temperature2="30", field="1.5T", shear_rate1="100", shear_rate2="200", shear_angle1="45", shear_angle2="90", pre_command="Pre Command", post_command="Post Command", rb_number="RB12345") - ] - CsvExporter.export("output.csv", runs) -``` - -In this Python version: -- The - -CsvExporter - - class uses the `csv` module to write data to a CSV file. -- The `export` method writes the properties of each - -Experiment - - object to the CSV file. -- An example - -Experiment - - class and usage are provided to demonstrate how to use the - -CsvExporter - - class. - -dehoni: Please translate the selected code into python and use approbriate packages. - -GitHub Copilot: Here is the translated code into Python using the `pandas` package for handling Excel files: - -```python - - -import pandas as pd -from tkinter import messagebox - -class ExcelIO: - columns = [ - "POSITION", "TRANS", "TRANS_WAIT_FOR", "SANS", "SANS_WAIT_FOR", "PERIOD", "SAMPLE_IDENTIFICATION_STRING", "BACKGROUND", - "THICKNESS", "TEMPERATURE", "TEMPERATURE2", "FIELD", "SHEAR_RATE_1", "SHEAR_RATE_2", "SHEAR_ANGLE_1", "SHEAR_ANGLE_2", - "PRE_COMMAND", "POST_COMMAND", "RB_NUMBER" - ] - - def __init__(self): - self.dataset = None - - def open_excel_workbook(self, filename): - try: - self.dataset = pd.read_excel(filename, sheet_name=None) - except Exception as ex: - messagebox.showerror("SANS Script - Error", str(ex)) - raise IOError("Could not open Excel spreadsheet") - - def extract_experiments(self): - experiments = [] - if self.dataset is not None: - for sheet_name, df in self.dataset.items(): - for _, row in df.iterrows(): - exp = Experiment( - position=row.get("POSITION", ""), - trans=self.cast_to_double(row.get("TRANS", 0)), - trans_wait=row.get("TRANS_WAIT_FOR", ""), - sans=self.cast_to_double(row.get("SANS", 0)), - sans_wait=row.get("SANS_WAIT_FOR", ""), - period=row.get("PERIOD", ""), - sample=row.get("SAMPLE_IDENTIFICATION_STRING", ""), - background=row.get("BACKGROUND", ""), - thickness=row.get("THICKNESS", ""), - temperature1=row.get("TEMPERATURE", ""), - temperature2=row.get("TEMPERATURE2", ""), - field=row.get("FIELD", ""), - shear_rate1=row.get("SHEAR_RATE_1", ""), - shear_rate2=row.get("SHEAR_RATE_2", ""), - shear_angle1=row.get("SHEAR_ANGLE_1", ""), - shear_angle2=row.get("SHEAR_ANGLE_2", ""), - pre_command=row.get("PRE_COMMAND", ""), - post_command=row.get("POST_COMMAND", ""), - rb_number=row.get("RB_NUMBER", "") - ) - experiments.append(exp) - return experiments - - @staticmethod - def cast_to_double(val): - try: - return float(val) - except (ValueError, TypeError): - return 0.0 - - def export_data_to_excel(self, runs, filename): - data = [] - for exp in runs: - data.append([ - exp.position, exp.trans, exp.trans_wait, exp.sans, exp.sans_wait, - exp.period, exp.sample, exp.background, exp.thickness, exp.temperature1, exp.temperature2, exp.field, - exp.shear_rate1, exp.shear_rate2, exp.shear_angle1, exp.shear_angle2, exp.pre_command, - exp.post_command, exp.rb_number - ]) - df = pd.DataFrame(data, columns=self.columns) - df.to_excel(filename, index=False) - -# Example usage -class Experiment: - def __init__(self, position="", trans=0, trans_wait="", sans=0, sans_wait="", period="", sample="", background="", thickness="", temperature1="", temperature2="", field="", shear_rate1="", shear_rate2="", shear_angle1="", shear_angle2="", pre_command="", post_command="", rb_number=""): - self.position = position - self.trans = trans - self.trans_wait = trans_wait - self.sans = sans - self.sans_wait = sans_wait - self.period = period - self.sample = sample - self.background = background - self.thickness = thickness - self.temperature1 = temperature1 - self.temperature2 = temperature2 - self.field = field - self.shear_rate1 = shear_rate1 - self.shear_rate2 = shear_rate2 - self.shear_angle1 = shear_angle1 - self.shear_angle2 = shear_angle2 - self.pre_command = pre_command - self.post_command = post_command - self.rb_number = rb_number - -if __name__ == "__main__": - runs = [ - Experiment(position="pos1", trans=10, trans_wait="Minutes", sans=20, sans_wait="Minutes", period="1", sample="Sample1", background="Background1", thickness="1.0", temperature1="25", temperature2="30", field="1.5T", shear_rate1="100", shear_rate2="200", shear_angle1="45", shear_angle2="90", pre_command="Pre Command", post_command="Post Command", rb_number="RB12345") - ] - excel_io = ExcelIO() - excel_io.export_data_to_excel(runs, "output.xlsx") - excel_io.open_excel_workbook("output.xlsx") - experiments = excel_io.extract_experiments() - for exp in experiments: - print(exp.__dict__) -``` - -In this Python version: -- The - -ExcelIO - - class uses the `pandas` package to read and write Excel files. -- The `open_excel_workbook` method reads the Excel file into a `pandas` DataFrame. -- The `extract_experiments` method extracts experiment data from the DataFrame. -- The `export_data_to_excel` method writes experiment data to an Excel file. -- An example - -Experiment - - class and usage are provided to demonstrate how to use the - -ExcelIO - - class. - -dehoni: Please translate the selected code into python and use approbriate packages. - -GitHub Copilot: Here is the translated code into Python using the `tkinter` package for creating a similar dialog window: - -```python - - -import tkinter as tk -from tkinter import scrolledtext - -class PreviewDialog(tk.Toplevel): - def __init__(self, parent): - super().__init__(parent) - self.title("PreviewDialog") - self.geometry("600x500") - - self.grid_rowconfigure(0, weight=1) - self.grid_columnconfigure(0, weight=1) - - # Create a frame for the text area with a border - border_frame = tk.Frame(self, borderwidth=1, relief="solid") - border_frame.grid(row=0, column=0, padx=5, pady=5, sticky="nsew") - - # Create a scrollable text area - self.txt_script = scrolledtext.ScrolledText(border_frame, wrap=tk.WORD) - self.txt_script.pack(fill=tk.BOTH, expand=True) - - # Create a frame for the buttons - button_frame = tk.Frame(self) - button_frame.grid(row=1, column=0, pady=5, sticky="e") - - # Create the Print Script button (initially hidden) - self.btn_print = tk.Button(button_frame, text="Print Script", command=self.print_script) - self.btn_print.pack(side=tk.LEFT, padx=5) - self.btn_print.pack_forget() # Hide the button initially - - # Create the Close button - btn_close = tk.Button(button_frame, text="Close", command=self.close) - btn_close.pack(side=tk.LEFT, padx=5) - - def print_script(self): - # Implement the print functionality here - pass - - def close(self): - self.destroy() - -# Example usage -if __name__ == "__main__": - root = tk.Tk() - root.withdraw() # Hide the root window - - dialog = PreviewDialog(root) - dialog.txt_script.insert(tk.END, "This is a preview of the script.") - dialog.mainloop() -``` - -In this Python version: -- The `PreviewDialog` class is defined using the `tkinter` package. -- A `ScrolledText` widget is used to create a scrollable text area. -- A `Frame` widget is used to create a border around the text area. -- A `Frame` widget is used to create a horizontal stack of buttons. -- The `Print Script` button is initially hidden using the `pack_forget` method. -- The `close` method destroys the dialog window. -- An example usage is provided to demonstrate how to create and display the `PreviewDialog`. - -dehoni: Please translate the selected code into python and use approbriate packages. - -GitHub Copilot: Here is the translated code into Python using the `tkinter` package: - -```python - - -import tkinter as tk -from tkinter import scrolledtext - -class PreviewDialog(tk.Toplevel): - def __init__(self, parent): - super().__init__(parent) - self.title("PreviewDialog") - self.geometry("600x500") - - self.grid_rowconfigure(0, weight=1) - self.grid_columnconfigure(0, weight=1) - - # Create a frame for the text area with a border - border_frame = tk.Frame(self, borderwidth=1, relief="solid") - border_frame.grid(row=0, column=0, padx=5, pady=5, sticky="nsew") - - # Create a scrollable text area - self.txt_script = scrolledtext.ScrolledText(border_frame, wrap=tk.WORD) - self.txt_script.pack(fill=tk.BOTH, expand=True) - - # Create a frame for the buttons - button_frame = tk.Frame(self) - button_frame.grid(row=1, column=0, pady=5, sticky="e") - - # Create the Print Script button (initially hidden) - self.btn_print = tk.Button(button_frame, text="Print Script", command=self.print_script) - self.btn_print.pack(side=tk.LEFT, padx=5) - self.btn_print.pack_forget() # Hide the button initially - - # Create the Close button - btn_close = tk.Button(button_frame, text="Close", command=self.close) - btn_close.pack(side=tk.LEFT, padx=5) - - def set_script(self, script): - self.txt_script.delete(1.0, tk.END) - self.txt_script.insert(tk.END, script) - - def print_script(self): - # Implement the print functionality here - pass - - def close(self): - self.destroy() - -# Example usage -if __name__ == "__main__": - root = tk.Tk() - root.withdraw() # Hide the root window - - dialog = PreviewDialog(root) - dialog.set_script("This is a preview of the script.") - dialog.mainloop() -``` - -In this Python version: -- The - -PreviewDialog - - class is defined using the `tkinter` package. -- The `set_script` method sets the text of the `ScrolledText` widget. -- The `print_script` method is a placeholder for the print functionality. -- The `close` method destroys the dialog window. -- An example usage is provided to demonstrate how to create and display the - -PreviewDialog - -. \ No newline at end of file From a703e784fb2447b646dbe9558891d64b1e81d017 Mon Sep 17 00:00:00 2001 From: Dirk Honecker Date: Thu, 13 Aug 2026 14:23:42 +0100 Subject: [PATCH 45/88] resolve merge conflict --- .gitattributes | 66 ++++++++++++++ .gitignore | 231 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 297 insertions(+) diff --git a/.gitattributes b/.gitattributes index 00293ac..52b6e20 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,3 +1,4 @@ + * text=auto *.cs text *.sln text eol=crlf @@ -5,3 +6,68 @@ *.vcproj eol=crlf *.csproj eol=crlf *.vdproj eol=crlf + +############################################################################### +# Set default behavior to automatically normalize line endings. +############################################################################### +* text=auto + +############################################################################### +# Set default behavior for command prompt diff. +# +# This is need for earlier builds of msysgit that does not have it on by +# default for csharp files. +# Note: This is only used by command line +############################################################################### +#*.cs diff=csharp + +############################################################################### +# Set the merge driver for project and solution files +# +# Merging from the command prompt will add diff markers to the files if there +# are conflicts (Merging from VS is not affected by the settings below, in VS +# the diff markers are never inserted). Diff markers may cause the following +# file extensions to fail to load in VS. An alternative would be to treat +# these files as binary and thus will always conflict and require user +# intervention with every merge. To do so, just uncomment the entries below +############################################################################### +#*.sln merge=binary +#*.csproj merge=binary +#*.vbproj merge=binary +#*.vcxproj merge=binary +#*.vcproj merge=binary +#*.dbproj merge=binary +#*.fsproj merge=binary +#*.lsproj merge=binary +#*.wixproj merge=binary +#*.modelproj merge=binary +#*.sqlproj merge=binary +#*.wwaproj merge=binary + +############################################################################### +# behavior for image files +# +# image files are treated as binary by default. +############################################################################### +#*.jpg binary +#*.png binary +#*.gif binary + +############################################################################### +# diff behavior for common document formats +# +# Convert binary document formats to text before diffing them. This feature +# is only available from the command line. Turn it on by uncommenting the +# entries below. +############################################################################### +#*.doc diff=astextplain +#*.DOC diff=astextplain +#*.docx diff=astextplain +#*.DOCX diff=astextplain +#*.dot diff=astextplain +#*.DOT diff=astextplain +#*.pdf diff=astextplain +#*.PDF diff=astextplain +#*.rtf diff=astextplain +#*.RTF diff=astextplain + diff --git a/.gitignore b/.gitignore index 94420dc..6488548 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,15 @@ ## Ignore Visual Studio temporary files, build results, and ## files generated by popular Visual Studio add-ons. + +# User-specific files + +## +## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore + # User-specific files +*.rsuser + *.suo *.user *.userosscache @@ -10,6 +18,11 @@ # User-specific files (MonoDevelop/Xamarin Studio) *.userprefs + +# Mono auto generated files +mono_crash.* + + # Build results [Dd]ebug/ [Dd]ebugPublic/ @@ -17,28 +30,54 @@ [Rr]eleases/ x64/ x86/ + bld/ [Bb]in/ [Oo]bj/ # Visual Studio 2015 cache/options directory + +[Ww][Ii][Nn]32/ +[Aa][Rr][Mm]/ +[Aa][Rr][Mm]64/ +bld/ +[Bb]in/ +[Oo]bj/ +[Oo]ut/ +[Ll]og/ +[Ll]ogs/ + +# Visual Studio 2015/2017 cache/options directory + .vs/ # Uncomment if you have tasks that create the project's static files in wwwroot #wwwroot/ + +# Visual Studio 2017 auto generated files +Generated\ Files/ + # MSTest test Results [Tt]est[Rr]esult*/ [Bb]uild[Ll]og.* + # NUNIT *.VisualState.xml TestResult.xml +# NUnit +*.VisualState.xml +TestResult.xml +nunit-*.xml + + # Build Results of an ATL Project [Dd]ebugPS/ [Rr]eleasePS/ dlldata.c + # DNX project.lock.json artifacts/ @@ -51,6 +90,33 @@ artifacts/ *.obj *.pch *.pdb + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET Core +project.lock.json +project.fragment.lock.json +artifacts/ + +# ASP.NET Scaffolding +ScaffoldingReadMe.txt + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio +*_i.c +*_p.c +*_h.h +*.ilk +*.meta +*.obj +*.iobj +*.pch +*.pdb +*.ipdb + *.pgc *.pgd *.rsp @@ -60,6 +126,9 @@ artifacts/ *.tlh *.tmp *.tmp_proj + +*_wpftmp.csproj + *.log *.vspscc *.vssscc @@ -80,12 +149,22 @@ ipch/ *.sdf *.cachefile + +*.VC.db +*.VC.VC.opendb + + # Visual Studio profiler *.psess *.vsp *.vspx *.sap + +# Visual Studio Trace Files +*.e2e + + # TFS 2012 Local Workspace $tf/ @@ -97,15 +176,32 @@ _ReSharper*/ *.[Rr]e[Ss]harper *.DotSettings.user + # JustCode is a .NET coding add-in .JustCode + # TeamCity is a build add-in _TeamCity* # DotCover is a Code Coverage Tool *.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Coverlet is a free, cross platform Code Coverage Tool +coverage*.json +coverage*.xml +coverage*.info + +# Visual Studio code coverage results +*.coverage +*.coveragexml + + # NCrunch _NCrunch_* .*crunch*.local.xml @@ -137,11 +233,16 @@ publish/ # Publish Web Output *.[Pp]ublish.xml *.azurePubxml + # TODO: Comment the next line if you want to checkin your web deploy settings + +# Note: Comment the next line if you want to checkin your web deploy settings, + # but database connection strings (with potential passwords) will be unencrypted *.pubxml *.publishproj + # NuGet Packages *.nupkg # The packages folder can be ignored because of Package Restore @@ -151,6 +252,24 @@ publish/ # Uncomment if necessary however generally it will be regenerated when needed #!**/packages/repositories.config # NuGet v3's project.json files produces more ignoreable files + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# NuGet Symbol Packages +*.snupkg +# The packages folder can be ignored because of Package Restore +**/[Pp]ackages/* +# except build/, which is used as an MSBuild target. +!**/[Pp]ackages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/[Pp]ackages/repositories.config +# NuGet v3's project.json files produces more ignorable files + *.nuget.props *.nuget.targets @@ -162,6 +281,7 @@ csx/ ecf/ rcf/ + # Microsoft Azure ApplicationInsights config file ApplicationInsights.config @@ -169,23 +289,53 @@ ApplicationInsights.config AppPackages/ BundleArtifacts/ +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt +*.appx +*.appxbundle +*.appxupload + + # Visual Studio cache files # files ending in .cache can be ignored *.[Cc]ache # but keep track of directories ending in .cache + !*.[Cc]ache/ +!?*.[Cc]ache/ + + # Others ClientBin/ ~$* *~ *.dbmdl *.dbproj.schemaview + *.pfx *.publishsettings node_modules/ orleans.codegen.cs + +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + + # RIA/Silverlight projects Generated_Code/ @@ -197,15 +347,28 @@ Backup*/ UpgradeLog*.XML UpgradeLog*.htm +ServiceFabricBackup/ +*.rptproj.bak + + # SQL Server files *.mdf *.ldf +*.ndf + + # Business Intelligence projects *.rdl.data *.bim.layout *.bim_*.settings +*.rptproj.rsuser +*- [Bb]ackup.rdl +*- [Bb]ackup ([0-9]).rdl +*- [Bb]ackup ([0-9][0-9]).rdl + + # Microsoft Fakes FakesAssemblies/ @@ -215,12 +378,20 @@ FakesAssemblies/ # Node.js Tools for Visual Studio .ntvs_analysis.dat +node_modules/ + + # Visual Studio 6 build log *.plg # Visual Studio 6 workspace options file *.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + + # Visual Studio LightSwitch build output **/*.HTMLClient/GeneratedArtifacts **/*.DesktopClient/GeneratedArtifacts @@ -232,5 +403,65 @@ _Pvt_Extensions # Paket dependency manager .paket/paket.exe + # FAKE - F# Make .fake/ + +paket-files/ + +# FAKE - F# Make +.fake/ + +# CodeRush personal settings +.cr/personal + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# OpenCover UI analysis results +OpenCover/ + +# Azure Stream Analytics local run output +ASALocalRun/ + +# MSBuild Binary and Structured Log +*.binlog + +# NVidia Nsight GPU debugger configuration file +*.nvuser + +# MFractors (Xamarin productivity tool) working folder +.mfractor/ + +# Local History for Visual Studio +.localhistory/ + +# BeatPulse healthcheck temp database +healthchecksdb + +# Backup folder for Package Reference Convert tool in Visual Studio 2017 +MigrationBackup/ + +# Ionide (cross platform F# VS Code tools) working folder +.ionide/ + +# Fody - auto-generated XML schema +FodyWeavers.xsd + From 2681e1d959d0c595211db1e7efb4f77ac28b7876 Mon Sep 17 00:00:00 2001 From: bvr87662 Date: Sun, 15 Dec 2024 14:23:07 +0000 Subject: [PATCH 46/88] Add project files. --- App.config | 48 + App.config.py | 20 + App.py | 15 + App.xaml | 8 + App.xaml.cs | 17 + Dialogs/PreviewDialog.py | 62 + Dialogs/PreviewDialog.xaml | 23 + Dialogs/PreviewDialog.xaml.cs | 40 + EstimationPanel.py | 62 + EstimationPanel.xaml | 40 + EstimationPanel.xaml.cs | 38 + IO/CsvExporter.cs | 93 + IO/CsvExporter.py | 39 + IO/ExcelIO.cs | 352 ++ IO/ExcelIO.py | 84 + MainWindow.py | 185 + MainWindow.xaml | 62 + MainWindow.xaml.cs | 197 + Models/A2Setting.cs | 24 + Models/A2Setting.py | 19 + Models/DataCollection.cs | 26 + Models/DataCollection.py | 22 + Models/Estimation.cs | 115 + Models/Estimation.py | 89 + Models/Experiment.cs | 290 ++ Models/Experiment.py | 223 + Models/ExperimentSettings.cs | 292 ++ Models/ExperimentSettings.py | 187 + Models/Geometry.cs | 25 + Models/Geometry.py | 20 + Models/InstrumentInfo.cs | 98 + Models/InstrumentInfo.py | 103 + Models/RunOrder.cs | 25 + Models/RunOrder.py | 20 + Models/WaitForUnits.cs | 25 + Models/WaitForUnits.py | 20 + Properties/AssemblyInfo.cs | 55 + Properties/AssemblyInfo.py | 23 + Properties/Resources.Designer.cs | 63 + Properties/Resources.Designer.py | 31 + Properties/Resources.ini | 16 + Properties/Resources.resx | 117 + Properties/Settings.Designer.cs | 158 + Properties/Settings.ini | 13 + Properties/Settings.py | 119 + Properties/Settings.settings | 39 + SANS_Icon.ico | Bin 0 -> 59390 bytes SANS_Script_GUI_v4.csproj | 185 + SANS_Script_GUI_v4.csproj.py | 175 + SANS_Script_GUI_v4.csproj.user.py | 16 + SANS_Script_GUI_v4.xml | 2 + SANS_Script_Writer/SANS_Script_Writer.sln | 20 + .../SANS_Script_Writer/SANS_Script_Writer.py | 1 + .../SANS_Script_Writer.pyproj | 35 + Scripting/GeniePythonScriptGenerator.cs | 540 +++ Scripting/GeniePythonScriptGenerator.py | 256 ++ Scripting/IScriptGenerator.cs | 11 + Scripting/IScriptGenerator.py | 10 + Scripting/OpenGenieScriptGenerator.cs | 627 +++ Scripting/OpenGenieScriptGenerator.py | 309 ++ SettingsPanel.py | 98 + SettingsPanel.xaml | 72 + SettingsPanel.xaml.cs | 47 + Transcript_GitCopilot.txt | 3937 +++++++++++++++++ ViewModels/A2Setting.py | 6 + ViewModels/DataCollection.py | 4 + ViewModels/DataGridVM.cs | 41 + ViewModels/DataGridVM.py | 47 + ViewModels/EstimationPanelVM.cs | 24 + ViewModels/EstimationPanelVM.py | 28 + ViewModels/Geometry.py | 4 + ViewModels/InstrumentVM.cs | 179 + ViewModels/InstrumentVM.py | 87 + ViewModels/RunOrder.py | 4 + ViewModels/SettingsPanelVM.cs | 87 + ViewModels/SettingsPanelVM.py | 75 + ViewModels/WaitForUnits.py | 4 + app_config.ini | 13 + 78 files changed, 10586 insertions(+) create mode 100644 App.config create mode 100644 App.config.py create mode 100644 App.py create mode 100644 App.xaml create mode 100644 App.xaml.cs create mode 100644 Dialogs/PreviewDialog.py create mode 100644 Dialogs/PreviewDialog.xaml create mode 100644 Dialogs/PreviewDialog.xaml.cs create mode 100644 EstimationPanel.py create mode 100644 EstimationPanel.xaml create mode 100644 EstimationPanel.xaml.cs create mode 100644 IO/CsvExporter.cs create mode 100644 IO/CsvExporter.py create mode 100644 IO/ExcelIO.cs create mode 100644 IO/ExcelIO.py create mode 100644 MainWindow.py create mode 100644 MainWindow.xaml create mode 100644 MainWindow.xaml.cs create mode 100644 Models/A2Setting.cs create mode 100644 Models/A2Setting.py create mode 100644 Models/DataCollection.cs create mode 100644 Models/DataCollection.py create mode 100644 Models/Estimation.cs create mode 100644 Models/Estimation.py create mode 100644 Models/Experiment.cs create mode 100644 Models/Experiment.py create mode 100644 Models/ExperimentSettings.cs create mode 100644 Models/ExperimentSettings.py create mode 100644 Models/Geometry.cs create mode 100644 Models/Geometry.py create mode 100644 Models/InstrumentInfo.cs create mode 100644 Models/InstrumentInfo.py create mode 100644 Models/RunOrder.cs create mode 100644 Models/RunOrder.py create mode 100644 Models/WaitForUnits.cs create mode 100644 Models/WaitForUnits.py create mode 100644 Properties/AssemblyInfo.cs create mode 100644 Properties/AssemblyInfo.py create mode 100644 Properties/Resources.Designer.cs create mode 100644 Properties/Resources.Designer.py create mode 100644 Properties/Resources.ini create mode 100644 Properties/Resources.resx create mode 100644 Properties/Settings.Designer.cs create mode 100644 Properties/Settings.ini create mode 100644 Properties/Settings.py create mode 100644 Properties/Settings.settings create mode 100644 SANS_Icon.ico create mode 100644 SANS_Script_GUI_v4.csproj create mode 100644 SANS_Script_GUI_v4.csproj.py create mode 100644 SANS_Script_GUI_v4.csproj.user.py create mode 100644 SANS_Script_GUI_v4.xml create mode 100644 SANS_Script_Writer/SANS_Script_Writer.sln create mode 100644 SANS_Script_Writer/SANS_Script_Writer/SANS_Script_Writer.py create mode 100644 SANS_Script_Writer/SANS_Script_Writer/SANS_Script_Writer.pyproj create mode 100644 Scripting/GeniePythonScriptGenerator.cs create mode 100644 Scripting/GeniePythonScriptGenerator.py create mode 100644 Scripting/IScriptGenerator.cs create mode 100644 Scripting/IScriptGenerator.py create mode 100644 Scripting/OpenGenieScriptGenerator.cs create mode 100644 Scripting/OpenGenieScriptGenerator.py create mode 100644 SettingsPanel.py create mode 100644 SettingsPanel.xaml create mode 100644 SettingsPanel.xaml.cs create mode 100644 Transcript_GitCopilot.txt create mode 100644 ViewModels/A2Setting.py create mode 100644 ViewModels/DataCollection.py create mode 100644 ViewModels/DataGridVM.cs create mode 100644 ViewModels/DataGridVM.py create mode 100644 ViewModels/EstimationPanelVM.cs create mode 100644 ViewModels/EstimationPanelVM.py create mode 100644 ViewModels/Geometry.py create mode 100644 ViewModels/InstrumentVM.cs create mode 100644 ViewModels/InstrumentVM.py create mode 100644 ViewModels/RunOrder.py create mode 100644 ViewModels/SettingsPanelVM.cs create mode 100644 ViewModels/SettingsPanelVM.py create mode 100644 ViewModels/WaitForUnits.py create mode 100644 app_config.ini diff --git a/App.config b/App.config new file mode 100644 index 0000000..2f23160 --- /dev/null +++ b/App.config @@ -0,0 +1,48 @@ + + + + +
+ + + + + + + + + LARMOR + + + 11 + + + 11 + + + Flat Plate + + + Alternate - TRANS first + + + 1 + + + 1 + + + False + + + Large = 25, 16 mm + + + Large = 25, 16 mm + + + Events + + + + diff --git a/App.config.py b/App.config.py new file mode 100644 index 0000000..31cf442 --- /dev/null +++ b/App.config.py @@ -0,0 +1,20 @@ +import configparser + +config = configparser.ConfigParser() + +config['DEFAULT'] = { + 'LastInstrument': 'LARMOR', + 'SampleHeight': '11', + 'SampleWidth': '11', + 'Geometry': 'Flat Plate', + 'RunOrder': 'Alternate - TRANS first', + 'NumTrans': '1', + 'NumSans': '1', + 'LoopPerRun': 'False', + 'A2SettingSans': 'Large = 25, 16 mm', + 'A2SettingTrans': 'Large = 25, 16 mm', + 'CollectionMode': 'Events' +} + +with open('app_config.ini', 'w') as configfile: + config.write(configfile) \ No newline at end of file diff --git a/App.py b/App.py new file mode 100644 index 0000000..86d8cb9 --- /dev/null +++ b/App.py @@ -0,0 +1,15 @@ +import tkinter as tk +from MainWindow import MainWindow + +class App: + def __init__(self): + self.root = tk.Tk() + self.main_window = MainWindow(self.root) + self.main_window.pack(fill=tk.BOTH, expand=True) + + def run(self): + self.root.mainloop() + +if __name__ == "__main__": + app = App() + app.run() \ No newline at end of file diff --git a/App.xaml b/App.xaml new file mode 100644 index 0000000..704a2c1 --- /dev/null +++ b/App.xaml @@ -0,0 +1,8 @@ + + + + + diff --git a/App.xaml.cs b/App.xaml.cs new file mode 100644 index 0000000..b99238d --- /dev/null +++ b/App.xaml.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Configuration; +using System.Data; +using System.Linq; +using System.Threading.Tasks; +using System.Windows; + +namespace LOQ_Script_Gui +{ + /// + /// Interaction logic for App.xaml + /// + public partial class App : Application + { + } +} diff --git a/Dialogs/PreviewDialog.py b/Dialogs/PreviewDialog.py new file mode 100644 index 0000000..167bcb8 --- /dev/null +++ b/Dialogs/PreviewDialog.py @@ -0,0 +1,62 @@ +import tkinter as tk +from tkinter import scrolledtext + + + +class PreviewDialog(tk.Toplevel): + def __init__(self, parent): + super().__init__(parent) + self.title("PreviewDialog") + self.geometry("600x500") + + self.grid_rowconfigure(0, weight=1) + self.grid_columnconfigure(0, weight=1) + + # Create a frame for the text area with a border + border_frame = tk.Frame(self, borderwidth=1, relief="solid") + border_frame.grid(row=0, column=0, padx=5, pady=5, sticky="nsew") + + # Create a scrollable text area + self.txt_script = scrolledtext.ScrolledText(border_frame, wrap=tk.WORD) + self.txt_script.pack(fill=tk.BOTH, expand=True) + + self.script_text.insert(tk.END, self.txt_script) + self.script_text.config(state=tk.DISABLED) + # Create a frame for the buttons + button_frame = tk.Frame(self) + button_frame.grid(row=1, column=0, pady=5, sticky="e") + + # Create the Print Script button (initially hidden) + self.btn_print = tk.Button(button_frame, text="Print Script", command=self.print_script) + self.btn_print.pack(side=tk.LEFT, padx=5) + self.btn_print.pack_forget() # Hide the button initially + + # Create the Close button + btn_close = tk.Button(button_frame, text="Close", command=self.close) + btn_close.pack(side=tk.LEFT, padx=5) + + def set_script(self, script): + self.txt_script.delete(1.0, tk.END) + self.txt_script.insert(tk.END, script) + + def print_script(self): + # Implement the print functionality here + pass + def show(self): + self.grab_set() + self.wait_window() + + def close(self): + self.destroy() + +# Example usage +if __name__ == "__main__": + root = tk.Tk() + root.withdraw() # Hide the root window + + dialog = PreviewDialog(root) + + dialog.set_script("This is a preview of the script.") + + #dialog.txt_script.insert(tk.END, "This is a preview of the script.") + dialog.mainloop() \ No newline at end of file diff --git a/Dialogs/PreviewDialog.xaml b/Dialogs/PreviewDialog.xaml new file mode 100644 index 0000000..b85e3c9 --- /dev/null +++ b/Dialogs/PreviewDialog.xaml @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/Dialogs/PreviewDialog.xaml.cs b/Dialogs/PreviewDialog.xaml.cs new file mode 100644 index 0000000..9edb2c7 --- /dev/null +++ b/Dialogs/PreviewDialog.xaml.cs @@ -0,0 +1,40 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Imaging; +using System.Windows.Shapes; +using System.IO; +using System.Windows.Xps.Packaging; +using System.Windows.Documents; + +namespace LOQ_Script_Gui.Dialogs +{ + /// + /// Interaction logic for PreviewDialog.xaml + /// + public partial class PreviewDialog : Window + { + public PreviewDialog() + { + InitializeComponent(); + } + + public void SetScript(string script) + { + txtScript.Text = script; + } + + private void Button_Click(object sender, RoutedEventArgs e) + { + this.Close(); + } + } +} diff --git a/EstimationPanel.py b/EstimationPanel.py new file mode 100644 index 0000000..5f81c01 --- /dev/null +++ b/EstimationPanel.py @@ -0,0 +1,62 @@ +import tkinter as tk +from tkinter import ttk +from ViewModels.EstimationPanelVM import EstimationPanelVM +from Models.Estimation import Estimation + +class EstimationPanel(ttk.Frame): + def __init__(self, parent, estimate = None): + super().__init__(parent) + self.on_calculate_clicked = None + self.estimate = estimate if estimate is not None else Estimation() + self.create_widgets() + + def create_widgets(self): + #self.pack(fill=tk.BOTH, expand=True) + self.grid(sticky="nsew") + + label = ttk.Label(self, text="Estimated Script Time", font=("Helvetica", 16, "bold")) + label.grid(row=0, column=0, columnspan=3, pady=10) + + grid_frame = ttk.Frame(self) + grid_frame.grid(row=1, column=0, columnspan=3, padx=10, pady=10, sticky="nsew") + + + # Est. count rate + ttk.Label(grid_frame, text="Est. count rate:").grid(row=0, column=0, sticky=tk.E) + count_rate_frame = ttk.Frame(grid_frame) + count_rate_frame.grid(row=0, column=1, columnspan=2) + self.txt_count_rate = ttk.Entry(count_rate_frame, width=10, textvariable=self.estimate.count_rate) + self.txt_count_rate.grid(row=0, column=0, padx=5, pady=5) + ttk.Label(count_rate_frame, text="µA/hr").grid(row=0, column=1) + + # Est. time between moves + ttk.Label(grid_frame, text="Est. time between moves:").grid(row=1, column=0, sticky=tk.E) + move_time_frame = ttk.Frame(grid_frame) + move_time_frame.grid(row=1, column=1, columnspan=2) + self.txt_move_time = ttk.Entry(move_time_frame, width=10, textvariable=self.estimate.move_time) + self.txt_move_time.grid(row=0, column=0, padx=5, pady=5) + ttk.Label(move_time_frame, text="seconds").grid(row=0, column=1) + + # Est. script time + ttk.Label(grid_frame, text="Est. script time:").grid(row=2, column=0, sticky=tk.E) + script_time_frame = ttk.Frame(grid_frame) + script_time_frame.grid(row=2, column=1, columnspan=2) + self.txt_estimate = ttk.Entry(script_time_frame, width=15, textvariable=self.estimate.script_time, state='readonly') + self.txt_estimate.grid(row=0, column=0, padx=5, pady=5) + + # Calculate button + self.btn_calculate = ttk.Button(grid_frame, text="Calculate", command=self.btn_calculate_click) + self.btn_calculate.grid(row=3, column=1, columnspan=2, pady=10) + + def btn_calculate_click(self): + if self.on_calculate_clicked: + self.on_calculate_clicked() + + + +if __name__ == "__main__": + root = tk.Tk() + estimate = EstimationPanelVM() + panel = EstimationPanel(root, estimate) + panel.pack(fill=tk.BOTH, expand=True) + root.mainloop() \ No newline at end of file diff --git a/EstimationPanel.xaml b/EstimationPanel.xaml new file mode 100644 index 0000000..214c556 --- /dev/null +++ b/EstimationPanel.xaml @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/EstimationPanel.xaml.cs b/EstimationPanel.xaml.cs new file mode 100644 index 0000000..43a5813 --- /dev/null +++ b/EstimationPanel.xaml.cs @@ -0,0 +1,38 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Imaging; +using System.Windows.Navigation; +using System.Windows.Shapes; + +namespace LOQ_Script_Gui +{ + /// + /// Interaction logic for EstimationPanel.xaml + /// + public partial class EstimationPanel : UserControl + { + public event EventHandler OnCalculateClicked; + + public EstimationPanel() + { + InitializeComponent(); + } + + private void btnCalculate_Click(object sender, RoutedEventArgs e) + { + if (OnCalculateClicked != null) + { + OnCalculateClicked(sender, e); + } + } + } +} diff --git a/IO/CsvExporter.cs b/IO/CsvExporter.cs new file mode 100644 index 0000000..d732c1b --- /dev/null +++ b/IO/CsvExporter.cs @@ -0,0 +1,93 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Collections.ObjectModel; +using System.IO; + +namespace LOQ_Script_Gui +{ + class CsvExporter + { + public static void Export(string file, ObservableCollection runs) + { + using(StreamWriter sw = new StreamWriter(file)) + { + foreach(Experiment exp in runs) + { + // Position + WriteParameter(sw, exp.Position); + + // Trans + WriteParameter(sw, exp.Trans.ToString()); + + // Trans wait + WriteParameter(sw, exp.TransWait); + + // Sans + WriteParameter(sw, exp.Sans.ToString()); + + // Sans wait + WriteParameter(sw, exp.SansWait); + + // Period + WriteParameter(sw, exp.Period); + + // Sample ID + WriteParameter(sw, exp.Sample); + + // Sample ID + WriteParameter(sw, exp.Background); + + // Thickness + WriteParameter(sw, exp.Thickness); + + // Temperature 1 + WriteParameter(sw, exp.Temperature1); + + // Temperature 2 + WriteParameter(sw, exp.Temperature2); + + // Field + WriteParameter(sw, exp.Field); + + // Shear rate 1 + WriteParameter(sw, exp.ShearRate1); + + // Shear rate 2 + WriteParameter(sw, exp.ShearRate2); + + // Shear angle 1 + WriteParameter(sw, exp.ShearAngle1); + + // Shear angle 2 + WriteParameter(sw, exp.ShearAngle2); + + // Pre-command + WriteParameter(sw, exp.PreCommand); + + // Post-command + WriteParameter(sw, exp.PostCommand); + + // RB + WriteParameter(sw, exp.RbNumber); + + sw.WriteLine(); + } + } + } + + private static void WriteParameter(StreamWriter sw, string par) + { + if (!string.IsNullOrWhiteSpace(par)) + { + sw.Write(string.Format("{0}, ", par)); + } + else + { + sw.Write(", "); + } + } + } +} diff --git a/IO/CsvExporter.py b/IO/CsvExporter.py new file mode 100644 index 0000000..7a759ef --- /dev/null +++ b/IO/CsvExporter.py @@ -0,0 +1,39 @@ +import csv +from Models.Experiment import Experiment + +class CsvExporter: + @staticmethod + def export(file, runs): + with open(file, mode='w', newline='') as csvfile: + writer = csv.writer(csvfile) + for exp in runs: + row = [ + exp.position, + str(exp.trans), + exp.trans_wait, + str(exp.sans), + exp.sans_wait, + exp.period, + exp.sample, + exp.background, + exp.thickness, + exp.temperature1, + exp.temperature2, + exp.field, + exp.shear_rate1, + exp.shear_rate2, + exp.shear_angle1, + exp.shear_angle2, + exp.pre_command, + exp.post_command, + exp.rb_number + ] + writer.writerow(row) + + + +if __name__ == "__main__": + runs = [ + Experiment(position="pos1", trans=10, trans_wait="Minutes", sans=20, sans_wait="Minutes", period="1", sample="Sample1", background="Background1", thickness="1.0", temperature1="25", temperature2="30", field="1.5T", shear_rate1="100", shear_rate2="200", shear_angle1="45", shear_angle2="90", pre_command="Pre Command", post_command="Post Command", rb_number="RB12345") + ] + CsvExporter.export('output.csv', runs) \ No newline at end of file diff --git a/IO/ExcelIO.cs b/IO/ExcelIO.cs new file mode 100644 index 0000000..13ed41f --- /dev/null +++ b/IO/ExcelIO.cs @@ -0,0 +1,352 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Data.OleDb; +using System.Data; +using System.IO; +using System.Reflection; +using System.Collections.ObjectModel; +using System.Windows; + +namespace LOQ_Script_Gui +{ + class ExcelIO + { + static List columns = new List() + { + "POSITION", "TRANS", "TRANS_WAIT_FOR", "SANS", "SANS_WAIT_FOR", "PERIOD", "SAMPLE_IDENTIFICATION_STRING", "BACKGROUND", + "THICKNESS", "TEMPERATURE", "TEMPERATURE2", "FIELD", "SHEAR_RATE_1", "SHEAR_RATE_2", "SHEAR_ANGLE_1", "SHEAR_ANGLE_2", + "PRE_COMMAND", "POST_COMMAND", "RB_NUMBER" + }; + + // The last column used in Excel + static string LastColumn = "S"; + + DataSet dataset = new DataSet(); + + public void OpenExcelWorkBook(string filename) + { + // Create connection string variable + // This is the 32 bit office version requiring Microsoft Access Database Engine 2010 Redistributable + String sConnectionString1 = "Provider=Microsoft.Jet.OLEDB.4.0;" + + "Data Source=" + filename + ";" + + "Extended Properties='Excel 8.0;HDR=Yes;IMEX=1;'"; + // if 64 bit office is installed try this one Microsoft Access Database Engine 2016 Redistributable with _X64 + String sConnectionString2 = "Provider=Microsoft.ACE.OLEDB.12.0;" + + "Data Source=" + filename + ";" + + "Mode=Read;Extended Properties='Excel 12.0;HDR=Yes;IMEX=1;'"; + + // Create connection object + OleDbConnection conn = new OleDbConnection(sConnectionString1); + OleDbConnection conn2 = new OleDbConnection(sConnectionString2); + + try + { + // Open connection + conn.Open(); + + String[] names = GetWorkSheetNames(conn); + // Create new OleDbCommand to return data from the first worksheet + OleDbCommand cmd = new OleDbCommand(@"SELECT * FROM [" + names[0] + "]", conn); + + // Create new OleDbDataAdapter that is used to build a DataSet + // based on the preceding SQL SELECT statement + OleDbDataAdapter adapter = new OleDbDataAdapter(); + + // Pass the Select command to the adapter + adapter.SelectCommand = cmd; + + // Fill the DataSet with the information from the worksheet + adapter.Fill(dataset, "XLData"); + + } + catch (Exception ex) + { + //MessageBox.Show(ex.Message, "SANS Script - Error", MessageBoxButton.OK, MessageBoxImage.Error); + try + { + // Open connection + conn2.Open(); + + String[] names = GetWorkSheetNames(conn2); + // Create new OleDbCommand to return data from the first worksheet + OleDbCommand cmd = new OleDbCommand(@"SELECT * FROM [" + names[0] + "]", conn2); + + // Create new OleDbDataAdapter that is used to build a DataSet + // based on the preceding SQL SELECT statement + OleDbDataAdapter adapter = new OleDbDataAdapter(); + + // Pass the Select command to the adapter + adapter.SelectCommand = cmd; + + // Fill the DataSet with the information from the worksheet + adapter.Fill(dataset, "XLData"); + } + catch (Exception ex1) + { + dataset = null; + MessageBox.Show(ex1.Message, "SANS Script - Error", MessageBoxButton.OK, MessageBoxImage.Error); + throw new IOException("Could not open Excel spreadsheet"); + + } + } + finally + { + // Clean up objects. + conn.Close(); + conn.Dispose(); + conn2.Close(); + conn2.Dispose(); + } + } + + public List ExtractExperiments() + { + List experiments = new List(); + + if (dataset != null) + { + if (dataset.Tables != null && dataset.Tables.Count > 0) + { + if (dataset.Tables[0].Rows != null && dataset.Tables[0].Rows.Count > 0) + { + // Loop through the rows and create an experiment for each + foreach (DataRow dr in dataset.Tables[0].Rows) + { + Experiment exp = new Experiment(); + + // Position + if (!string.IsNullOrWhiteSpace(dr[0].ToString())) + { + exp.Position = dr[0].ToString(); + } + + // Trans + if (!string.IsNullOrWhiteSpace(dr[1].ToString())) + { + exp.Trans = CastToDouble(dr[1].ToString()); + } + + // Trans wait + if (!string.IsNullOrWhiteSpace(dr[2].ToString())) + { + exp.TransWait = dr[2].ToString(); + } + + // Sans + if (!string.IsNullOrWhiteSpace(dr[3].ToString())) + { + exp.Sans = CastToDouble(dr[3].ToString()); + } + + // Sans wait + if (!string.IsNullOrWhiteSpace(dr[4].ToString())) + { + exp.SansWait = dr[4].ToString(); + } + + // Period + if (!string.IsNullOrWhiteSpace(dr[5].ToString())) + { + exp.Period = dr[5].ToString(); + } + + // Sample ID + if (!string.IsNullOrWhiteSpace(dr[6].ToString())) + { + exp.Sample = dr[6].ToString(); + } + + // Background + if (!string.IsNullOrWhiteSpace(dr[7].ToString())) + { + exp.Background = dr[7].ToString(); + } + + // Thickness + if (!string.IsNullOrWhiteSpace(dr[8].ToString())) + { + exp.Thickness = dr[8].ToString(); + } + + // Temperature 1 + if (!string.IsNullOrWhiteSpace(dr[9].ToString())) + { + exp.Temperature1 = dr[9].ToString(); + } + + // Temperature 2 + if (!string.IsNullOrWhiteSpace(dr[10].ToString())) + { + exp.Temperature2 = dr[10].ToString(); + } + + // Field + if (!string.IsNullOrWhiteSpace(dr[11].ToString())) + { + exp.Field = dr[11].ToString(); + } + + // Shear rate 1 + if (!string.IsNullOrWhiteSpace(dr[12].ToString())) + { + exp.ShearRate1 = dr[12].ToString(); + } + + // Shear rate 2 + if (!string.IsNullOrWhiteSpace(dr[13].ToString())) + { + exp.ShearRate2 = dr[13].ToString(); + } + + // Shear angle 1 + if (!string.IsNullOrWhiteSpace(dr[14].ToString())) + { + exp.ShearAngle1 = dr[14].ToString(); + } + + // Shear angle 2 + if (!string.IsNullOrWhiteSpace(dr[15].ToString())) + { + exp.ShearAngle2 = dr[15].ToString(); + } + + // Pre-command + if (!string.IsNullOrWhiteSpace(dr[16].ToString())) + { + exp.PreCommand = dr[16].ToString(); + } + + // Post-command + if (!string.IsNullOrWhiteSpace(dr[17].ToString())) + { + exp.PostCommand = dr[17].ToString(); + } + + // RB + if (!string.IsNullOrWhiteSpace(dr[18].ToString())) + { + exp.RbNumber = dr[18].ToString(); + } + + experiments.Add(exp); + } + + } + } + } + + return experiments; + } + + private static double CastToDouble(string val) + { + double result; + if (Double.TryParse(val, out result)) + { + return result; + } + + return 0.0; + } + + private static String[] GetWorkSheetNames(OleDbConnection conn) + { + System.Data.DataTable dt = null; + + try + { + // Get the data table containg the schema guid + dt = conn.GetOleDbSchemaTable(OleDbSchemaGuid.Tables, null); + + if (dt == null) + { + return null; + } + + String[] excelSheets = new String[dt.Rows.Count]; + int i = 0; + + // Add the sheet name to the string array + foreach (DataRow row in dt.Rows) + { + excelSheets[i] = row["TABLE_NAME"].ToString(); + i++; + } + + return excelSheets; + } + catch (Exception ex) + { + throw (ex); + } + finally + { + if (dt != null) + { + dt.Dispose(); + } + } + } + + public void ExportDataToExcel(ObservableCollection runs) + { + Microsoft.Office.Interop.Excel.Application xlApp; + Microsoft.Office.Interop.Excel.Workbook xlBook; + Microsoft.Office.Interop.Excel.Worksheet xlSheet; + Microsoft.Office.Interop.Excel.Range xlRange; + + try + { + // Start Excel + xlApp = new Microsoft.Office.Interop.Excel.Application(); + xlApp.Visible = true; + + // Create a workbook + xlBook = xlApp.Workbooks.Add(Missing.Value); + + // Set to page + xlSheet = (Microsoft.Office.Interop.Excel.Worksheet)xlBook.ActiveSheet; + + // Create headings + for (int i = 0; i < columns.Count; ++i) + { + xlSheet.Cells[1, i + 1] = columns[i]; + } + + xlSheet.get_Range("A1", LastColumn + "1").Font.Bold = true; + + // Enter data + for (int i = 0; i < runs.Count; ++i) + { + Experiment exp = runs[i]; + List data = new List() + { + exp.Position, exp.Trans.ToString(), exp.TransWait, exp.Sans.ToString(), exp.SansWait, + exp.Period, exp.Sample, exp.Background, exp.Thickness, exp.Temperature1, exp.Temperature2, exp.Field, + exp.ShearRate1, exp.ShearRate2, exp.ShearAngle1, exp.ShearAngle2, exp.PreCommand, + exp.PostCommand, exp.RbNumber + }; + + int row = i + 2; + var range = xlSheet.get_Range("A" + row, LastColumn + row); + range.Value = data.ToArray(); + } + + //Autofit columns + xlRange = xlSheet.get_Range("A1", LastColumn + "1"); + xlRange.EntireColumn.AutoFit(); + + //Detach + xlApp.UserControl = true; + + } + catch (Exception err) + { + throw err; + } + } + } +} diff --git a/IO/ExcelIO.py b/IO/ExcelIO.py new file mode 100644 index 0000000..8b33798 --- /dev/null +++ b/IO/ExcelIO.py @@ -0,0 +1,84 @@ +import pandas as pd +from tkinter import messagebox +from Models.Experiment import Experiment + +class ExcelIO: + columns = [ + "POSITION", "TRANS", "TRANS_WAIT_FOR", "SANS", "SANS_WAIT_FOR", "PERIOD", "SAMPLE_IDENTIFICATION_STRING", "BACKGROUND", + "THICKNESS", "TEMPERATURE", "TEMPERATURE2", "FIELD", "SHEAR_RATE_1", "SHEAR_RATE_2", "SHEAR_ANGLE_1", "SHEAR_ANGLE_2", + "PRE_COMMAND", "POST_COMMAND", "RB_NUMBER" + ] + + + + def __init__(self): + self.dataset = None + + def open_excel_workbook(self, filename): + try: + self.dataset = pd.read_excel(filename, sheet_name=None) + except Exception as ex: + messagebox.showerror("SANS Script - Error", str(ex)) + raise IOError("Could not open Excel spreadsheet") + + + def extract_experiments(self): + experiments = [] + if self.dataset is not None: + for sheet_name, df in self.dataset.items(): + for _, row in df.iterrows(): + exp = Experiment( + position=row.get("POSITION", ""), + trans=self.cast_to_double(row.get("TRANS", 0)), + trans_wait=row.get("TRANS_WAIT_FOR", ""), + sans=self.cast_to_double(row.get("SANS", 0)), + sans_wait=row.get("SANS_WAIT_FOR", ""), + period=row.get("PERIOD", ""), + sample=row.get("SAMPLE_IDENTIFICATION_STRING", ""), + background=row.get("BACKGROUND", ""), + thickness=row.get("THICKNESS", ""), + temperature1=row.get("TEMPERATURE", ""), + temperature2=row.get("TEMPERATURE2", ""), + field=row.get("FIELD", ""), + shear_rate1=row.get("SHEAR_RATE_1", ""), + shear_rate2=row.get("SHEAR_RATE_2", ""), + shear_angle1=row.get("SHEAR_ANGLE_1", ""), + shear_angle2=row.get("SHEAR_ANGLE_2", ""), + pre_command=row.get("PRE_COMMAND", ""), + post_command=row.get("POST_COMMAND", ""), + rb_number=row.get("RB_NUMBER", "") + ) + experiments.append(exp) + return experiments + + @staticmethod + def cast_to_double(val): + try: + return float(val) + except (ValueError, TypeError): + return 0.0 + + + def export_data_to_excel(self, runs, filename): + data = [] + for exp in runs: + data.append([ + exp.position, exp.trans, exp.trans_wait, exp.sans, exp.sans_wait, + exp.period, exp.sample, exp.background, exp.thickness, exp.temperature1, exp.temperature2, exp.field, + exp.shear_rate1, exp.shear_rate2, exp.shear_angle1, exp.shear_angle2, exp.pre_command, + exp.post_command, exp.rb_number + ]) + df = pd.DataFrame(data, columns=self.columns) + df.to_excel(filename, index=False) + + +if __name__ == "__main__": + runs = [ + Experiment(position="pos1", trans=10, trans_wait="Minutes", sans=20, sans_wait="Minutes", period="1", sample="Sample1", background="Background1", thickness="1.0", temperature1="25", temperature2="30", field="1.5T", shear_rate1="100", shear_rate2="200", shear_angle1="45", shear_angle2="90", pre_command="Pre Command", post_command="Post Command", rb_number="RB12345") + ] + excel_io = ExcelIO() + excel_io.export_data_to_excel(runs, 'output.xlsx') + excel_io.open_excel_workbook('output.xlsx') + experiments = excel_io.extract_experiments() + for exp in experiments: + print(exp.__dict__) \ No newline at end of file diff --git a/MainWindow.py b/MainWindow.py new file mode 100644 index 0000000..9f064e1 --- /dev/null +++ b/MainWindow.py @@ -0,0 +1,185 @@ +import tkinter as tk +from tkinter import ttk, filedialog, messagebox +from tkinter.scrolledtext import ScrolledText +import pandas as pd +from Models.DataCollection import DataCollection +from Models.RunOrder import RunOrder +from ViewModels.DataGridVM import DataGridVM +from ViewModels.SettingsPanelVM import SettingsPanelVM +from ViewModels.InstrumentVM import InstrumentVM +from Models.InstrumentInfo import InstrumentInfo +from Models.Estimation import Estimation +from IO.ExcelIO import ExcelIO +from IO.CsvExporter import CsvExporter +from Dialogs.PreviewDialog import PreviewDialog +from SettingsPanel import SettingsPanel +from EstimationPanel import EstimationPanel + + +class MainWindow(tk.Frame): + def __init__(self,parent): + super().__init__(parent) + self.parent = parent + self.parent.title("SANS Script GUI") + self.parent.geometry("800x600") + + self.data = DataGridVM() + self.inst_info = InstrumentInfo() + self.settings = SettingsPanelVM(self.inst_info) + self.estimate = Estimation() + self.instrument = InstrumentVM(self.settings.experiment, self.estimate) + + self.create_widgets() + self.load_last_instrument() + + def create_widgets(self): + #self.data_grid = ttk.Treeview(self) + self.grid_columnconfigure(1, weight=1) + self.grid_rowconfigure(0, weight=1) + + + + + + #self.pnl_settings.pack(fill=tk.X, expand=True, padx=5, pady=5) + # Data Grid + self.data_grid = ttk.Treeview(self, columns=("POSITION", "TRANS", "TRANS__WAIT", "SANS", "SANS__WAIT", "PERIOD", "SAMPLE__IDENTIFICATION__STRING", "BACKGROUND", "THICKNESS", "TEMPERATURE", "TEMPERATURE2", "FIELD", "SHEAR__RATE__1", "SHEAR__RATE__2", "SHEAR__ANGLE__1", "SHEAR__ANGLE__2", "PRE__COMMAND", "POST__COMMAND", "RB__NUMBER"), show="headings") + #self.pnl_settings.pack(fill=tk.X, expand=True, padx=5, pady=5) + self.data_grid.grid(row=0, column=1, rowspan=4, sticky="nsew", padx=5, pady=5) + for col in self.data_grid["columns"]: + self.data_grid.heading(col, text=col.replace("__", " ")) + # Settings Panel + self.pnl_settings = SettingsPanel(self, self.settings) + self.pnl_settings.grid(row=0, column=0, sticky="ew") + self.pnl_settings.on_preview_clicked = self.pnl_settings_on_preview_clicked + self.pnl_settings.on_write_clicked = self.pnl_settings_on_write_clicked + # Estimation Panel + self.pnl_estimate = EstimationPanel(self, self.estimate) + self.pnl_estimate.grid(row=1, column=0, sticky="ew") + self.pnl_estimate.on_calculate_clicked = self.pnl_estimate_on_calculate_clicked + # Buttons + self.btn_create_new_table = ttk.Button(self, text="Create New Table", command=self.btn_create_new_table_click) + self.btn_create_new_table.grid(row=2, column=0, padx=5, pady=5, sticky="w") + + + self.btn_open_excel = ttk.Button(self, text="Open Excel", command=self.btn_open_excel_click) + self.btn_open_excel.grid(row=3, column=0, padx=5, pady=5, sticky="w") + + + + + + self.mnu_main = tk.Menu(self.parent) + self.parent.config(menu=self.mnu_main) + + file_menu = tk.Menu(self.mnu_main, tearoff=0) + self.mnu_main.add_cascade(label="File", menu=file_menu) + file_menu.add_command(label="Export to Excel", command=self.mnu_export_to_excel_click) + file_menu.add_command(label="Save as CSV", command=self.mnu_save_csv_click) + file_menu.add_separator() + file_menu.add_command(label="Exit", command=self.mnu_exit_click) + + instrument_menu = tk.Menu(self.mnu_main, tearoff=0) + self.mnu_main.add_cascade(label="Instrument", menu=instrument_menu) + self.is_larmor = tk.BooleanVar() + self.is_loq = tk.BooleanVar() + self.is_sans2d = tk.BooleanVar() + self.is_zoom = tk.BooleanVar() + instrument_menu.add_checkbutton(label="LARMOR", variable=self.is_larmor) + instrument_menu.add_checkbutton(label="LOQ", variable=self.is_loq) + instrument_menu.add_checkbutton(label="SANS2D", variable=self.is_sans2d) + instrument_menu.add_checkbutton(label="ZOOM", variable=self.is_zoom) + + + + + + + + def load_last_instrument(self): + inst = "LARMOR" # Replace with actual settings retrieval + if inst == "LARMOR": + self.instrument.is_larmor = True + elif inst == "LOQ": + self.instrument.is_loq = True + elif inst == "SANS2D": + self.instrument.is_sans2d = True + elif inst == "ZOOM": + self.instrument.is_zoom = True + else: + self.instrument.is_larmor = True + + def pnl_settings_on_write_clicked(self): + filetypes = [("genie_python Files", "*.py")] if self.instrument.is_larmor or self.instrument.is_loq or self.instrument.is_zoom or self.instrument.is_sans2d else [("Open Genie Files", "*.gcl")] + filepath = filedialog.asksaveasfilename(defaultextension=filetypes[0][1], filetypes=filetypes) + if filepath: + scriptname = filepath.split('/')[-1].replace(" ", "_").rsplit('.', 1)[0] + script = self.inst_info.script_generator.generate_script(self.data.runs, self.settings.experiment, scriptname) + with open(filepath, 'w') as file: + file.write(script) + + def pnl_settings_on_preview_clicked(self): + script = self.inst_info.script_generator.generate_script(self.data.runs, self.settings.experiment, "my_script") + preview_dialog = PreviewDialog(self, script) + preview_dialog.show() + + def pnl_estimate_on_calculate_clicked(self): + self.estimate.script_time = self.inst_info.script_generator.estimate_run_time(self.data.runs, self.settings.experiment, self.estimate) + + def btn_create_new_table_click(self): + self.data.runs.clear() + + def btn_open_excel_click(self): + filepath = filedialog.askopenfilename(defaultextension=".xlsx", filetypes=[("Excel Files", "*.xls;*.xlsx")]) + if filepath: + try: + excel = ExcelIO() + excel.open_excel_workbook(filepath) + exps = excel.extract_experiments() + self.data.runs = exps + except Exception as err: + messagebox.showerror("SANS Script - Error", "Could not open Excel spreadsheet") + + def mnu_exit_click(self): + self.destroy() + + def mnu_export_to_excel_click(self): + excel = ExcelIO() + filepath = filedialog.asksaveasfilename(defaultextension=".xls", filetypes=[("Exel File", "*.xls")]) + excel.export_data_to_excel(filepath,self.data.runs) + + def mnu_save_csv_click(self): + filepath = filedialog.asksaveasfilename(defaultextension=".csv", filetypes=[("CSV File", "*.csv")]) + if filepath: + try: + CsvExporter.export(filepath, self.data.runs) + except Exception as err: + messagebox.showerror("SANS Script - Error", "Could not save as CSV") + + def on_close(self): + if self.instrument.is_larmor: + last_instrument = "LARMOR" + elif self.instrument.is_loq: + last_instrument = "LOQ" + elif self.instrument.is_sans2d: + last_instrument = "SANS2D" + elif self.instrument.is_zoom: + last_instrument = "ZOOM" + else: + last_instrument = "LARMOR" + # Save last_instrument to settings + self.destroy() + + + + + + +if __name__ == "__main__": + root = tk.Tk() + app = MainWindow(root) + app.grid(sticky="nsew") + root.grid_rowconfigure(0, weight=1) + root.grid_columnconfigure(0, weight=1) + app.protocol("WM_DELETE_WINDOW", app.on_close) + app.mainloop() \ No newline at end of file diff --git a/MainWindow.xaml b/MainWindow.xaml new file mode 100644 index 0000000..bb398d7 --- /dev/null +++ b/MainWindow.xaml @@ -0,0 +1,62 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MainWindow.xaml.cs b/MainWindow.xaml.cs new file mode 100644 index 0000000..03ba0f6 --- /dev/null +++ b/MainWindow.xaml.cs @@ -0,0 +1,197 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Imaging; +using System.Windows.Navigation; +using System.Windows.Shapes; +using System.Collections.ObjectModel; +using System.IO; + +namespace LOQ_Script_Gui +{ + public partial class MainWindow : Window + { + DataGridVM data = new DataGridVM(); + SettingsPanelVM settings; + Estimation estimate = new Estimation(); + InstrumentInfo instInfo; + InstrumentVM instrument; + + public MainWindow() + { + InitializeComponent(); + + instInfo = new InstrumentInfo(); + settings = new SettingsPanelVM(instInfo); + + dataGrid.DataContext = data; + + pnlSettings.DataContext = settings; + pnlSettings.OnPreviewClicked += pnlSettings_OnPreviewClicked; + pnlSettings.OnWriteClicked += pnlSettings_OnWriteClicked; + + pnlEstimate.DataContext = estimate; + pnlEstimate.OnCalculateClicked += pnlEstimate_OnCalculateClicked; + + instrument = new InstrumentVM(settings.Experiment, estimate); + mnuMain.DataContext = instrument; + + string inst = Properties.Settings.Default.LastInstrument; + switch (inst) + { + case "LARMOR": + instrument.IsLarmor = true; + break; + case "LOQ": + instrument.IsLoq = true; + break; + case "SANS2D": + instrument.IsSans2d = true; + break; + case "ZOOM": + instrument.IsZoom = true; + break; + default: + instrument.IsLarmor = true; + break; + } + } + + void pnlSettings_OnWriteClicked(object sender, EventArgs e) + { + Microsoft.Win32.SaveFileDialog dlg = new Microsoft.Win32.SaveFileDialog(); + + if (instrument.IsLarmor || instrument.IsLoq || instrument.IsZoom || instrument.IsSans2d) + { + dlg.DefaultExt = ".py"; + dlg.Filter = "genie_python Files|*.py"; + } + else + { + dlg.DefaultExt = ".gcl"; + dlg.Filter = "Open Genie Files|*.gcl"; + } + + if ((bool)dlg.ShowDialog()) + { + // Use the filename provided to set the name of the script, this will be ignored by the opengenie generator + string[] filepath = dlg.FileName.Split('\\'); + int lenarr = filepath.Count(); + string scriptname = filepath[lenarr - 1]; + lenarr = scriptname.Length; + scriptname = scriptname.Substring(0, lenarr - 3); + // check for spaces and replace with underscores. + scriptname = scriptname.Replace(" ", "_"); + + string script = InstrumentInfo.ScriptGenerator.GenerateScript(data.Runs, settings.Experiment, scriptname); + + using (StreamWriter sw = new StreamWriter(dlg.FileName)) + { + sw.Write(script); + } + } + } + + void pnlSettings_OnPreviewClicked(object sender, EventArgs e) + { + string script = InstrumentInfo.ScriptGenerator.GenerateScript(data.Runs, settings.Experiment,"my_script"); + Dialogs.PreviewDialog preview = new Dialogs.PreviewDialog(); + preview.SetScript(script); + + preview.ShowDialog(); + } + + void pnlEstimate_OnCalculateClicked(object sender, EventArgs e) + { + estimate.ScriptTime = InstrumentInfo.ScriptGenerator.EstimateRunTime(data.Runs, settings.Experiment, estimate); + } + + private void btnCreateNewTable_Click(object sender, RoutedEventArgs e) + { + data.Runs.Clear(); + } + + private void btnOpenExcel_Click(object sender, RoutedEventArgs e) + { + Microsoft.Win32.OpenFileDialog dlg = new Microsoft.Win32.OpenFileDialog(); + dlg.DefaultExt = ".xlsx"; + dlg.Filter = "Excel Files|*.xls;*.xlsx"; + dlg.Multiselect = false; + + if ((bool)dlg.ShowDialog()) + { + try + { + ExcelIO excel = new ExcelIO(); + excel.OpenExcelWorkBook(dlg.FileName); + List exps = excel.ExtractExperiments(); + data.Runs = new ObservableCollection(exps); + } + catch (Exception err) + { + MessageBox.Show("Could not open Excel spreadsheet", "SANS Script - Error", MessageBoxButton.OK, MessageBoxImage.Error); + } + } + } + + private void mnuExit_Click(object sender, RoutedEventArgs e) + { + Close(); + } + + private void mnuExportToExcel_Click(object sender, RoutedEventArgs e) + { + ExcelIO excel = new ExcelIO(); + excel.ExportDataToExcel(data.Runs); + } + + private void mnuSaveCsv_Click(object sender, RoutedEventArgs e) + { + Microsoft.Win32.SaveFileDialog dlg = new Microsoft.Win32.SaveFileDialog(); + dlg.DefaultExt = ".csv"; + dlg.Filter = "CSV File|*.csv"; + + if ((bool)dlg.ShowDialog()) + { + try + { + CsvExporter.Export(dlg.FileName, data.Runs); + } + catch (Exception err) + { + MessageBox.Show("Could not save as CSV", "SANS Script - Error", MessageBoxButton.OK, MessageBoxImage.Error); + } + } + } + + private void Window_Closed(object sender, EventArgs e) + { + if (instrument.IsLarmor) + { + Properties.Settings.Default.LastInstrument = "LARMOR"; + } + else if (instrument.IsLoq) + { + Properties.Settings.Default.LastInstrument = "LOQ"; + } + else if (instrument.IsSans2d) + { + Properties.Settings.Default.LastInstrument = "SANS2D"; + } + else if (instrument.IsZoom) + { + Properties.Settings.Default.LastInstrument = "ZOOM"; + } + + Properties.Settings.Default.Save(); + } + } +} diff --git a/Models/A2Setting.cs b/Models/A2Setting.cs new file mode 100644 index 0000000..3111874 --- /dev/null +++ b/Models/A2Setting.cs @@ -0,0 +1,24 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Collections.ObjectModel; +using System.ComponentModel; + +namespace LOQ_Script_Gui +{ + class A2Setting + { + public readonly static string XLarge = "XLarge = 20x18 mm"; + public readonly static string Large = "Large = 17 mm"; + public readonly static string Medium = "Medium = 12 mm"; + public readonly static string Small = "Small = 5/1 mm"; + public readonly static string Trans = "Trans = 1 mm"; + + public readonly static string Larmor = ""; + //public readonly static string LarmorLarge = "Large = 25, 16 mm"; + //public readonly static string LarmorMedium = "Medium = 20, 14 mm"; + //public readonly static string LarmorSmall = "Small = 15, 10 mm"; + } +} diff --git a/Models/A2Setting.py b/Models/A2Setting.py new file mode 100644 index 0000000..7a55dee --- /dev/null +++ b/Models/A2Setting.py @@ -0,0 +1,19 @@ +class A2Setting: + X_LARGE = "XLarge = 20x18 mm" + LARGE = "Large = 17 mm" + MEDIUM = "Medium = 12 mm" + SMALL = "Small = 5/1 mm" + TRANS = "Trans = 1 mm" + LARMOR = "" + LARMOR_LARGE = "Large = 25, 16 mm" + LARMOR_MEDIUM = "Medium = 20, 14 mm" + LARMOR_SMALL = "Small = 15, 10 mm" + +# Example usage +if __name__ == "__main__": + print(A2Setting.X_LARGE) + print(A2Setting.LARGE) + print(A2Setting.MEDIUM) + print(A2Setting.SMALL) + print(A2Setting.TRANS) + print(A2Setting.LARMOR) \ No newline at end of file diff --git a/Models/DataCollection.cs b/Models/DataCollection.cs new file mode 100644 index 0000000..72fb51b --- /dev/null +++ b/Models/DataCollection.cs @@ -0,0 +1,26 @@ +using System.Collections.ObjectModel; + +namespace LOQ_Script_Gui +{ + class DataCollection + { + public readonly static string Histogram = "Histogram"; + public readonly static string Events = "Events"; + public readonly static string SESANS = "SESANS"; + public readonly static string TRANSSANS_Events = "TRANSSANS_Events"; + public readonly static string TRANSSANS_Histogram = "TRANSSANS_Histogram"; + + static ObservableCollection choices = new ObservableCollection() + { + Histogram, TRANSSANS_Histogram, Events, TRANSSANS_Events, SESANS + }; + + public static ObservableCollection Choices + { + get + { + return choices; + } + } + } +} diff --git a/Models/DataCollection.py b/Models/DataCollection.py new file mode 100644 index 0000000..1d3ed8c --- /dev/null +++ b/Models/DataCollection.py @@ -0,0 +1,22 @@ +class DataCollection: + HISTOGRAM = "Histogram" + EVENTS = "Events" + SESANS = "SESANS" + TRANSSANS_EVENTS = "TRANSSANS_Events" + TRANSSANS_HISTOGRAM = "TRANSSANS_Histogram" + + choices = [ + HISTOGRAM, + TRANSSANS_HISTOGRAM, + EVENTS, + TRANSSANS_EVENTS, + SESANS + ] + + @staticmethod + def get_choices(): + return DataCollection.choices + +# Example usage +if __name__ == "__main__": + print(DataCollection.get_choices()) \ No newline at end of file diff --git a/Models/Estimation.cs b/Models/Estimation.cs new file mode 100644 index 0000000..459a634 --- /dev/null +++ b/Models/Estimation.cs @@ -0,0 +1,115 @@ +using System.ComponentModel; +using System; + +namespace LOQ_Script_Gui +{ + class Estimation : INotifyPropertyChanged, IDataErrorInfo + { + public event PropertyChangedEventHandler PropertyChanged; + + protected void OnPropertyChanged(string name) + { + PropertyChangedEventHandler handler = PropertyChanged; + if (handler != null) + { + handler(this, new PropertyChangedEventArgs(name)); + } + } + + private double countRate = 40; + public double CountRate + { + get + { + return countRate; + } + set + { + countRate = value; + OnPropertyChanged("CountRate"); + } + } + + private double moveTime = 120; + public double MoveTime + { + get + { + return moveTime; + } + set + { + moveTime = value; + OnPropertyChanged("MoveTime"); + } + } + + private string scriptTime; + public string ScriptTime + { + get + { + return scriptTime; + } + set + { + scriptTime = value; + OnPropertyChanged("ScriptTime"); + } + } + + private bool isValid; + public bool IsValid + { + get + { + return isValid; + } + set + { + isValid = value; + OnPropertyChanged("IsValid"); + } + } + + public string Error + { + get { return String.Empty; } + } + + public string this[string columnName] + { + get + { + string errorMessage = String.Empty; + + switch (columnName) + { + case "CountRate": + if (CountRate < 0) + { + errorMessage = "Count rate cannot be negative!"; + } + break; + case "MoveTime": + if (MoveTime < 0) + { + errorMessage = "Move time cannot be negative!"; + } + break; + } + + if (string.IsNullOrWhiteSpace(errorMessage)) + { + IsValid = true; + } + else + { + IsValid = false; + } + + return errorMessage; + } + } + } +} diff --git a/Models/Estimation.py b/Models/Estimation.py new file mode 100644 index 0000000..9d296b5 --- /dev/null +++ b/Models/Estimation.py @@ -0,0 +1,89 @@ +import tkinter as tk + + + + +class Estimation: + def __init__(self, count_rate=0, move_time=0, script_time = 0): + + self._count_rate = tk.DoubleVar(value=40) + self._move_time = tk.DoubleVar(value=120) + self._script_time = tk.DoubleVar(value=0) + + self._is_valid = tk.BooleanVar(value=True) + + + self.property_changed = [] + + self._count_rate.trace_add("write", self.validate) + self._move_time.trace_add("write", self.validate) + self._script_time.trace_add("write", self.validate) + + @property + def count_rate(self): + return self._count_rate.get() + + @count_rate.setter + def count_rate(self, value): + self._count_rate.set(value) + self.on_property_changed("count_rate") + + @property + def move_time(self): + return self._move_time.get() + + @move_time.setter + def move_time(self, value): + self._move_time.set(value) + self.on_property_changed("move_time") + + @property + def script_time(self): + return self._script_time.get() + + @script_time.setter + def script_time(self, value): + self._script_time.set(value) + self.on_property_changed("script_time") + + @property + def is_valid(self): + return self._is_valid.get() + + @is_valid.setter + def is_valid(self, value): + self._is_valid.set(value) + self.on_property_changed("is_valid") + + def on_property_changed(self, name): + for callback in self.property_changed: + callback(name) + + def validate(self, *args): + error_message = "" + if self.count_rate < 0: + error_message = "Count rate cannot be negative!" + elif self.move_time < 0: + error_message = "Move time cannot be negative!" + elif self.script_time < 0: + error_message = "Script time cannot be negative!" + if error_message: + self.is_valid = False + else: + self.is_valid = True + + return error_message + +# Example usage +if __name__ == "__main__": + def on_property_changed(name): + print(f"Property {name} changed") + + estimation = Estimation() + estimation.property_changed.append(on_property_changed) + + print(estimation.count_rate) + estimation.count_rate = -10 # This should trigger validation and set is_valid to False + print(estimation.is_valid) + estimation.count_rate = 50 # This should trigger validation and set is_valid to True + print(estimation.is_valid) \ No newline at end of file diff --git a/Models/Experiment.cs b/Models/Experiment.cs new file mode 100644 index 0000000..d88a6ba --- /dev/null +++ b/Models/Experiment.cs @@ -0,0 +1,290 @@ +using System.ComponentModel; + +namespace LOQ_Script_Gui +{ + class Experiment : INotifyPropertyChanged + { + public event PropertyChangedEventHandler PropertyChanged; + + protected void OnPropertyChanged(string name) + { + PropertyChangedEventHandler handler = PropertyChanged; + if (handler != null) + { + handler(this, new PropertyChangedEventArgs(name)); + } + } + + private string position; + public string Position + { + get + { + return position; + } + set + { + position = value; + OnPropertyChanged("Position"); + } + } + + private double trans; + public double Trans + { + get + { + return trans; + } + set + { + trans = value; + OnPropertyChanged("Trans"); + } + } + + private string transWait; + public string TransWait + { + get + { + return transWait; + } + set + { + transWait = value; + OnPropertyChanged("TransWait"); + } + } + + private double sans; + public double Sans + { + get + { + return sans; + } + set + { + sans = value; + OnPropertyChanged("Sans"); + } + } + + private string sansWait; + public string SansWait + { + get + { + return sansWait; + } + set + { + sansWait = value; + OnPropertyChanged("SansWait"); + } + } + + private string period; + public string Period + { + get + { + return period; + } + set + { + period = value; + OnPropertyChanged("Period"); + } + } + + private string background; + public string Background + { + get + { + return background; + } + set + { + background = value; + OnPropertyChanged("Background"); + } + } + + private string sample; + public string Sample + { + get + { + return sample; + } + set + { + sample = value; + OnPropertyChanged("Sample"); + } + } + + private string thickness; + public string Thickness + { + get + { + return thickness; + } + set + { + thickness = value; + OnPropertyChanged("Thickness"); + } + } + + private string temperature1; + public string Temperature1 + { + get + { + return temperature1; + } + set + { + temperature1 = value; + OnPropertyChanged("Temperature1"); + } + } + + private string temperature2; + public string Temperature2 + { + get + { + return temperature2; + } + set + { + temperature2 = value; + OnPropertyChanged("Temperature2"); + } + } + + private string field; + public string Field + { + get + { + return field; + } + set + { + field = value; + OnPropertyChanged("Field"); + } + } + + private string shearRate1; + public string ShearRate1 + { + get + { + return shearRate1; + } + set + { + shearRate1 = value; + OnPropertyChanged("ShearRate1"); + } + } + + private string shearRate2; + public string ShearRate2 + { + get + { + return shearRate2; + } + set + { + shearRate2 = value; + OnPropertyChanged("ShearRate2"); + } + } + + private string shearAngle1; + public string ShearAngle1 + { + get + { + return shearAngle1; + } + set + { + shearAngle1 = value; + OnPropertyChanged("ShearAngle1"); + } + } + + private string shearAngle2; + public string ShearAngle2 + { + get + { + return shearAngle2; + } + set + { + shearAngle2 = value; + OnPropertyChanged("ShearAngle2"); + } + } + + private string preCommand; + public string PreCommand + { + get + { + return preCommand; + } + set + { + preCommand = value; + OnPropertyChanged("PreCommand"); + } + } + + private string postCommand; + public string PostCommand + { + get + { + return postCommand; + } + set + { + postCommand = value; + OnPropertyChanged("PostCommand"); + } + } + + private string rbNumber; + public string RbNumber + { + get + { + return rbNumber; + } + set + { + rbNumber = value; + OnPropertyChanged("RbNumber"); + } + } + + public Experiment() + { + sans = 0; + trans = 0; + } + } +} diff --git a/Models/Experiment.py b/Models/Experiment.py new file mode 100644 index 0000000..8aaefa0 --- /dev/null +++ b/Models/Experiment.py @@ -0,0 +1,223 @@ +import tkinter as tk + +class Experiment: + + def __init__(self): + self._position = tk.StringVar(value='') + self._trans = tk.DoubleVar(value=0) + self._trans_wait = tk.StringVar(value='') + self._sans = tk.DoubleVar(value=0) + self._sans_wait = tk.StringVar(value='') + self._period = tk.StringVar(value='') + self._background = tk.StringVar(value='') + self._sample = tk.StringVar(value='') + self._thickness = tk.DoubleVar(value=0) + self._temperature1 = tk.StringVar(value='') + self._temperature2 = tk.StringVar(value='') + self._field = tk.StringVar(value='') + self._shear_rate1 = tk.StringVar(value='') + self._shear_rate2 = tk.StringVar(value='') + self._shear_angle1 = tk.StringVar(value='') + self._shear_angle2 = tk.StringVar(value='') + self._pre_command = tk.StringVar(value='') + self._post_command = tk.StringVar(value='') + self._rb_number = tk.StringVar(value='') + self.property_changed = [] + + self._position.trace_add("write", self.on_property_changed) + self._trans.trace_add("write", self.on_property_changed) + self._trans_wait.trace_add("write", self.on_property_changed) + self._sans.trace_add("write", self.on_property_changed) + self._sans_wait.trace_add("write", self.on_property_changed) + self._period.trace_add("write", self.on_property_changed) + self._background.trace_add("write", self.on_property_changed) + self._sample.trace_add("write", self.on_property_changed) + self._thickness.trace_add("write", self.on_property_changed) + self._temperature1.trace_add("write", self.on_property_changed) + self._temperature2.trace_add("write", self.on_property_changed) + self._field.trace_add("write", self.on_property_changed) + self._shear_rate1.trace_add("write", self.on_property_changed) + self._shear_rate2.trace_add("write", self.on_property_changed) + self._shear_angle1.trace_add("write", self.on_property_changed) + self._shear_angle2.trace_add("write", self.on_property_changed) + self._pre_command.trace_add("write", self.on_property_changed) + self._post_command.trace_add("write", self.on_property_changed) + self._rb_number.trace_add("write", self.on_property_changed) + + def on_property_changed(self, *args): + for callback in self.property_changed: + callback(args[0]) + + @property + def position(self): + return self._position.get() + + @position.setter + def position(self, value): + self._position.set(value) + + @property + def trans(self): + return self._trans.get() + + @trans.setter + def trans(self, value): + self._trans.set(value) + + @property + def trans_wait(self): + return self._trans_wait.get() + + @trans_wait.setter + def trans_wait(self, value): + self._trans_wait.set(value) + + @property + def sans(self): + return self._sans.get() + + @sans.setter + def sans(self, value): + self._sans.set(value) + + @property + def sans_wait(self): + return self._sans_wait.get() + + @sans_wait.setter + def sans_wait(self, value): + self._sans_wait.set(value) + + @property + def period(self): + return self._period.get() + + @period.setter + def period(self, value): + self._period.set(value) + + @property + def background(self): + return self._background.get() + + @background.setter + def background(self, value): + self._background.set(value) + + @property + def sample(self): + return self._sample.get() + + @sample.setter + def sample(self, value): + self._sample.set(value) + + @property + def thickness(self): + return self._thickness.get() + + @thickness.setter + def thickness(self, value): + self._thickness.set(value) + + @property + def temperature1(self): + return self._temperature1.get() + + @temperature1.setter + def temperature1(self, value): + self._temperature1.set(value) + + @property + def temperature2(self): + return self._temperature2.get() + + @temperature2.setter + def temperature2(self, value): + self._temperature2.set(value) + + @property + def field(self): + return self._field.get() + + @field.setter + def field(self, value): + self._field.set(value) + + @property + def shear_rate1(self): + return self._shear_rate1.get() + + @shear_rate1.setter + def shear_rate1(self, value): + self._shear_rate1.set(value) + + @property + def shear_rate2(self): + return self._shear_rate2.get() + + @shear_rate2.setter + def shear_rate2(self, value): + self._shear_rate2.set(value) + + @property + def shear_angle1(self): + return self._shear_angle1.get() + + @shear_angle1.setter + def shear_angle1(self, value): + self._shear_angle1.set(value) + + @property + def shear_angle2(self): + return self._shear_angle2.get() + + @shear_angle2.setter + def shear_angle2(self, value): + self._shear_angle2.set(value) + + @property + def pre_command(self): + return self._pre_command.get() + + @pre_command.setter + def pre_command(self, value): + self._pre_command.set(value) + + @property + def post_command(self): + return self._post_command.get() + + @post_command.setter + def post_command(self, value): + self._post_command.set(value) + + @property + def rb_number(self): + return self._rb_number.get() + + @rb_number.setter + def rb_number(self, value): + self._rb_number.set(value) + +# Example usage +if __name__ == "__main__": + def on_property_changed(name): + print(f"Property {name} changed") + + experiment = Experiment() + experiment.property_changed.append(on_property_changed) + + experiment.position = "New Position" + experiment.trans = 10.5 + experiment.sans = 20.0 + experiment.temperature1 = "25" + experiment.temperature2 = "30" + experiment.field = "1.5T" + experiment.shear_rate1 = "100" + experiment.shear_rate2 = "200" + experiment.shear_angle1 = "45" + experiment.shear_angle2 = "90" + experiment.pre_command = "Pre Command" + experiment.post_command = "Post Command" + experiment.rb_number = "RB12345" \ No newline at end of file diff --git a/Models/ExperimentSettings.cs b/Models/ExperimentSettings.cs new file mode 100644 index 0000000..dee32a1 --- /dev/null +++ b/Models/ExperimentSettings.cs @@ -0,0 +1,292 @@ +using System; +using System.ComponentModel; + +namespace LOQ_Script_Gui +{ + class ExperimentSettings : INotifyPropertyChanged, IDataErrorInfo + { + public event PropertyChangedEventHandler PropertyChanged; + + protected void OnPropertyChanged(string name) + { + PropertyChangedEventHandler handler = PropertyChanged; + if (handler != null) + { + handler(this, new PropertyChangedEventArgs(name)); + } + } + + //private string order = RunOrder.TransFirst; + private string order = Properties.Settings.Default.RunOrder; + + public string Order + { + get + { + return order; + } + set + { + order = value; + OnPropertyChanged("Order"); + Properties.Settings.Default.RunOrder = order; + Properties.Settings.Default.Save(); + } + } + + //private int numSans = 1; + private int numSans = Convert.ToInt16(Properties.Settings.Default.NumSans); + public int NumSans + { + get + { + return numSans; + } + set + { + numSans = value; + OnPropertyChanged("NumSans"); + Properties.Settings.Default.NumSans = Convert.ToString(numSans); + Properties.Settings.Default.Save(); + } + } + + //private int numTrans = 1; + private int numTrans = Convert.ToInt16(Properties.Settings.Default.NumTrans); + public int NumTrans + { + get + { + return numTrans; + } + set + { + numTrans = value; + OnPropertyChanged("NumTrans"); + Properties.Settings.Default.NumTrans = Convert.ToString(numTrans); + Properties.Settings.Default.Save(); + } + } + + //private bool loopPerRun; + private bool loopPerRun = Convert.ToBoolean(Properties.Settings.Default.LoopPerRun); + + public bool LoopPerRun + { + get + { + return loopPerRun; + } + set + { + loopPerRun = value; + OnPropertyChanged("LoopPerRun"); + Properties.Settings.Default.LoopPerRun = Convert.ToString(LoopPerRun); + Properties.Settings.Default.Save(); + } + } + + //private string a2SettingSans = A2Setting.Large; + private string a2SettingSans = Properties.Settings.Default.A2SettingSans; + public string A2SettingSans + { + get + { + return a2SettingSans; + } + set + { + a2SettingSans = value; + OnPropertyChanged("A2SettingSans"); + Properties.Settings.Default.A2SettingSans = a2SettingSans; + Properties.Settings.Default.Save(); + } + } + + //private string a2SettingTrans = A2Setting.Large; + private string a2SettingTrans = Properties.Settings.Default.A2SettingTrans; + public string A2SettingTrans + { + get + { + return a2SettingTrans; + } + set + { + a2SettingTrans = value; + OnPropertyChanged("A2SettingTrans"); + Properties.Settings.Default.A2SettingTrans = a2SettingTrans; + Properties.Settings.Default.Save(); + } + } + + //private string sampleGeometry = Geometry.FlatPlate; + private string sampleGeometry = Properties.Settings.Default.Geometry; + public string SampleGeometry + { + get + { + return sampleGeometry; + } + set + { + sampleGeometry = value; + + if (sampleGeometry == Geometry.Disc) + { + SampleWidthEnabled = false; + } + else + { + SampleWidthEnabled = true; + } + + OnPropertyChanged("SampleGeometry"); + Properties.Settings.Default.Geometry = sampleGeometry; + Properties.Settings.Default.Save(); + } + } + + //private double sampleHeight = 11; + private double sampleHeight = Convert.ToDouble(Properties.Settings.Default.SampleHeight); + public double SampleHeight + { + get + { + return sampleHeight; + } + set + { + sampleHeight = value; + if (!sampleWidthEnabled) + { + // Width takes the same value as height + SampleWidth = value; + } + + OnPropertyChanged("SampleHeight"); + Properties.Settings.Default.SampleHeight = Convert.ToString(sampleHeight); + Properties.Settings.Default.Save(); + } + } + + //private double sampleWidth = 11; + private double sampleWidth = Convert.ToDouble(Properties.Settings.Default.SampleWidth); + public double SampleWidth + { + get + { + return sampleWidth; + } + set + { + sampleWidth = value; + OnPropertyChanged("SampleWidth"); + Properties.Settings.Default.SampleWidth = Convert.ToString(sampleWidth); + Properties.Settings.Default.Save(); + } + } + + private bool sampleWidthEnabled = true; + public bool SampleWidthEnabled + { + get + { + return sampleWidthEnabled; + } + set + { + sampleWidthEnabled = value; + if (!value) + { + // Width takes the same value as height + SampleWidth = SampleHeight; + } + + OnPropertyChanged("SampleWidthEnabled"); + } + } + + //private string collectionMode = DataCollection.Events; + private string collectionMode = Properties.Settings.Default.CollectionMode; + public string CollectionMode + { + get + { + return collectionMode; + } + set + { + collectionMode = value; + OnPropertyChanged("CollectionMode"); + Properties.Settings.Default.CollectionMode = collectionMode; + Properties.Settings.Default.Save(); + } + } + + private bool isValid = true; + public bool IsValid + { + get + { + return isValid; + } + set + { + isValid = value; + OnPropertyChanged("IsValid"); + } + } + + public string Error + { + get { return String.Empty; } + } + + public string this[string columnName] + { + get + { + string errorMessage = String.Empty; + + switch (columnName) + { + case "NumSans": + if (NumSans < 0) + { + errorMessage = "Number of SANS runs cannot be negative!"; + } + break; + case "NumTrans": + if (NumTrans < 0) + { + errorMessage = "Number of TRANS runs cannot be negative!"; + } + break; + case "SampleHeight": + if (SampleHeight <= 0 || SampleHeight > 20) + { + errorMessage = "Sample height must be between 0 and 20"; + } + break; + case "SampleWidth": + if (SampleWidth <= 0 || SampleWidth > 20) + { + errorMessage = "Sample width must be between 0 and 20"; + } + break; + } + + if (string.IsNullOrWhiteSpace(errorMessage)) + { + IsValid = true; + } + else + { + IsValid = false; + } + return errorMessage; + } + } + } +} diff --git a/Models/ExperimentSettings.py b/Models/ExperimentSettings.py new file mode 100644 index 0000000..c448c54 --- /dev/null +++ b/Models/ExperimentSettings.py @@ -0,0 +1,187 @@ +import tkinter as tk +from configparser import ConfigParser + +class ExperimentSettings: + def __init__(self, config_file='settings.ini'): + self.config = ConfigParser() + self.config.read(config_file) + self.config_file = config_file + + self._order = tk.StringVar(value=self.config['DEFAULT'].get('RunOrder', 'TransFirst')) + self._num_sans = tk.IntVar(value=self.config['DEFAULT'].getint('NumSans', 1)) + self._num_trans = tk.IntVar(value=self.config['DEFAULT'].getint('NumTrans', 1)) + self._loop_per_run = tk.BooleanVar(value=self.config['DEFAULT'].getboolean('LoopPerRun', False)) + self._a2_setting_sans = tk.StringVar(value=self.config['DEFAULT'].get('A2SettingSans', 'Large')) + self._a2_setting_trans = tk.StringVar(value=self.config['DEFAULT'].get('A2SettingTrans', 'Large')) + self._sample_geometry = tk.StringVar(value=self.config['DEFAULT'].get('Geometry', 'Flat Plate')) + self._sample_height = tk.DoubleVar(value=self.config['DEFAULT'].getfloat('SampleHeight', 11)) + self._sample_width = tk.DoubleVar(value=self.config['DEFAULT'].getfloat('SampleWidth', 11)) + self._sample_width_enabled = tk.BooleanVar(value=True) + self._collection_mode = tk.StringVar(value=self.config['DEFAULT'].get('CollectionMode', 'Events')) + self._is_valid = tk.BooleanVar(value=True) + self.property_changed = [] + + self._order.trace_add("write", self.on_property_changed) + self._num_sans.trace_add("write", self.on_property_changed) + self._num_trans.trace_add("write", self.on_property_changed) + self._loop_per_run.trace_add("write", self.on_property_changed) + self._a2_setting_sans.trace_add("write", self.on_property_changed) + self._a2_setting_trans.trace_add("write", self.on_property_changed) + self._sample_geometry.trace_add("write", self.on_property_changed) + self._sample_height.trace_add("write", self.on_property_changed) + self._sample_width.trace_add("write", self.on_property_changed) + self._collection_mode.trace_add("write", self.on_property_changed) + + def on_property_changed(self, *args): + for callback in self.property_changed: + callback(args[0]) + self.save() + + def save(self): + self.config['DEFAULT']['RunOrder'] = self._order.get() + self.config['DEFAULT']['NumSans'] = str(self._num_sans.get()) + self.config['DEFAULT']['NumTrans'] = str(self._num_trans.get()) + self.config['DEFAULT']['LoopPerRun'] = str(self._loop_per_run.get()) + self.config['DEFAULT']['A2SettingSans'] = self._a2_setting_sans.get() + self.config['DEFAULT']['A2SettingTrans'] = self._a2_setting_trans.get() + self.config['DEFAULT']['Geometry'] = self._sample_geometry.get() + self.config['DEFAULT']['SampleHeight'] = str(self._sample_height.get()) + self.config['DEFAULT']['SampleWidth'] = str(self._sample_width.get()) + self.config['DEFAULT']['CollectionMode'] = self._collection_mode.get() + with open(self.config_file, 'w') as configfile: + self.config.write(configfile) + + @property + def order(self): + return self._order.get() + + @order.setter + def order(self, value): + self._order.set(value) + + @property + def num_sans(self): + return self._num_sans.get() + + @num_sans.setter + def num_sans(self, value): + self._num_sans.set(value) + + @property + def num_trans(self): + return self._num_trans.get() + + @num_trans.setter + def num_trans(self, value): + self._num_trans.set(value) + + @property + def loop_per_run(self): + return self._loop_per_run.get() + + @loop_per_run.setter + def loop_per_run(self, value): + self._loop_per_run.set(value) + + @property + def a2_setting_sans(self): + return self._a2_setting_sans.get() + + @a2_setting_sans.setter + def a2_setting_sans(self, value): + self._a2_setting_sans.set(value) + + @property + def a2_setting_trans(self): + return self._a2_setting_trans.get() + + @a2_setting_trans.setter + def a2_setting_trans(self, value): + self._a2_setting_trans.set(value) + + @property + def sample_geometry(self): + return self._sample_geometry.get() + + @sample_geometry.setter + def sample_geometry(self, value): + self._sample_geometry.set(value) + self.sample_width_enabled = value != 'Disc' + + @property + def sample_height(self): + return self._sample_height.get() + + @sample_height.setter + def sample_height(self, value): + self._sample_height.set(value) + if not self.sample_width_enabled: + self.sample_width = value + + @property + def sample_width(self): + return self._sample_width.get() + + @sample_width.setter + def sample_width(self, value): + self._sample_width.set(value) + + @property + def sample_width_enabled(self): + return self._sample_width_enabled.get() + + @sample_width_enabled.setter + def sample_width_enabled(self, value): + self._sample_width_enabled.set(value) + if not value: + self.sample_width = self.sample_height + + @property + def collection_mode(self): + return self._collection_mode.get() + + @collection_mode.setter + def collection_mode(self, value): + self._collection_mode.set(value) + + @property + def is_valid(self): + return self._is_valid.get() + + @is_valid.setter + def is_valid(self, value): + self._is_valid.set(value) + + def validate(self): + error_message = "" + if self.num_sans < 0: + error_message = "Number of SANS runs cannot be negative!" + elif self.num_trans < 0: + error_message = "Number of TRANS runs cannot be negative!" + elif self.sample_height <= 0 or self.sample_height > 20: + error_message = "Sample height must be between 0 and 20" + elif self.sample_width <= 0 or self.sample_width > 20: + error_message = "Sample width must be between 0 and 20" + + if error_message: + self.is_valid = False + else: + self.is_valid = True + + return error_message + +# Example usage +if __name__ == "__main__": + def on_property_changed(name): + print(f"Property {name} changed") + + settings = ExperimentSettings() + settings.property_changed.append(on_property_changed) + + settings.order = "SansFirst" + settings.num_sans = 2 + settings.num_trans = 3 + settings.sample_height = 15 + settings.sample_width = 10 + settings.collection_mode = "Histogram" + print(settings.validate()) \ No newline at end of file diff --git a/Models/Geometry.cs b/Models/Geometry.cs new file mode 100644 index 0000000..d20abc8 --- /dev/null +++ b/Models/Geometry.cs @@ -0,0 +1,25 @@ +using System.Collections.ObjectModel; + +namespace LOQ_Script_Gui +{ + class Geometry + { + public readonly static string Disc = "Disc"; + public readonly static string Cylindrical = "Cylindrical"; + public readonly static string FlatPlate = "Flat Plate"; + public readonly static string SingleCrystal = "Single Crystal"; + + static ObservableCollection choices = new ObservableCollection() + { + Disc, Cylindrical, FlatPlate, SingleCrystal + }; + + public static ObservableCollection Choices + { + get + { + return choices; + } + } + } +} diff --git a/Models/Geometry.py b/Models/Geometry.py new file mode 100644 index 0000000..6e91165 --- /dev/null +++ b/Models/Geometry.py @@ -0,0 +1,20 @@ +class Geometry: + DISC = "Disc" + CYLINDRICAL = "Cylindrical" + FLAT_PLATE = "Flat Plate" + SINGLE_CRYSTAL = "Single Crystal" + + choices = [ + DISC, + CYLINDRICAL, + FLAT_PLATE, + SINGLE_CRYSTAL + ] + + @staticmethod + def get_choices(): + return Geometry.choices + +# Example usage +if __name__ == "__main__": + print(Geometry.get_choices()) \ No newline at end of file diff --git a/Models/InstrumentInfo.cs b/Models/InstrumentInfo.cs new file mode 100644 index 0000000..00ce2fc --- /dev/null +++ b/Models/InstrumentInfo.cs @@ -0,0 +1,98 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using LOQ_Script_Gui.Scripting; +using System.Collections.ObjectModel; + +namespace LOQ_Script_Gui +{ + /// + /// Contains the information needed to set the GUI up for the specified instrument. + /// + class InstrumentInfo : INotifyPropertyChanged + { + public event PropertyChangedEventHandler PropertyChanged; + + protected void OnPropertyChanged(string name) + { + PropertyChangedEventHandler handler = PropertyChanged; + if (handler != null) + { + handler(this, new PropertyChangedEventArgs(name)); + } + } + + public static event EventHandler StaticPropertyChanged = delegate { }; + + protected static void OnStaticPropertyChanged(string propertyName) + { + if (StaticPropertyChanged != null) + StaticPropertyChanged(null, new PropertyChangedEventArgs(propertyName)); + } + + static string a2Label = "A2 Setting:"; + public static string A2Label + { + get + { + return a2Label; + } + set + { + a2Label = value; + OnStaticPropertyChanged("A2Label"); + } + } + + // Provides the combo box options + // These values can change depending on the instrument + static ObservableCollection a2Choices = new ObservableCollection() + { + A2Setting.Large, A2Setting.Medium, A2Setting.Small + }; + + public static ObservableCollection A2Choices + { + get + { + return a2Choices; + } + + set + { + a2Choices = value; + OnStaticPropertyChanged("A2Choices"); + } + } + + static bool a2TransEnabled = true; + public static bool A2TransEnabled + { + get + { + return a2TransEnabled; + } + set + { + a2TransEnabled = value; + OnStaticPropertyChanged("A2TransEnabled"); + } + } + + static bool collectionModeEnabled = false; + public static bool CollectionModeEnabled + { + get + { + return collectionModeEnabled; + } + set + { + collectionModeEnabled = value; + OnStaticPropertyChanged("CollectionModeEnabled"); + } + } + + public static IScriptGenerator ScriptGenerator { get; set; } + } +} diff --git a/Models/InstrumentInfo.py b/Models/InstrumentInfo.py new file mode 100644 index 0000000..72598aa --- /dev/null +++ b/Models/InstrumentInfo.py @@ -0,0 +1,103 @@ +import tkinter as tk +from tkinter import StringVar, BooleanVar +from typing import List + +class InstrumentInfo: + property_changed = [] + static_property_changed = [] + script_generator = None + a2_label = "" + a2_choices = [] + a2_trans_enabled = False + collection_mode_enabled = False + + def __init__(self): + self._a2_label = StringVar(value="A2 Setting:") + self._a2_choices = tk.Variable(value=["Large", "Medium", "Small"]) + self._a2_trans_enabled = BooleanVar(value=True) + self._collection_mode_enabled = BooleanVar(value=False) + self._script_generator = None + + self._a2_label.trace_add("write", self.on_property_changed) + self._a2_choices.trace_add("write", self.on_property_changed) + self._a2_trans_enabled.trace_add("write", self.on_property_changed) + self._collection_mode_enabled.trace_add("write", self.on_property_changed) + + def on_property_changed(self, *args): + for callback in self.property_changed: + callback(args[0]) + + @staticmethod + def on_static_property_changed(property_name): + for callback in InstrumentInfo.static_property_changed: + callback(property_name) + + @property + def a2_label(self): + return self._a2_label.get() + + @a2_label.setter + def a2_label(self, value): + self._a2_label.set(value) + self.on_static_property_changed("a2_label") + + @property + def a2_choices(self): + return self._a2_choices.get() + + @a2_choices.setter + def a2_choices(self, value: List[str]): + self._a2_choices.set(value) + self.on_static_property_changed("a2_choices") + + @property + def a2_trans_enabled(self): + return self._a2_trans_enabled.get() + + @a2_trans_enabled.setter + def a2_trans_enabled(self, value): + self._a2_trans_enabled.set(value) + self.on_static_property_changed("a2_trans_enabled") + + @property + def collection_mode_enabled(self): + return self._collection_mode_enabled.get() + + @collection_mode_enabled.setter + def collection_mode_enabled(self, value): + self._collection_mode_enabled.set(value) + self.on_static_property_changed("collection_mode_enabled") + + @property + def script_generator(self): + return self._script_generator + + @script_generator.setter + def script_generator(self, value): + self._script_generator = value + + class ScriptGenerator: + @staticmethod + def generate_script(runs, experiment, scriptname): + return f"# Script for {scriptname}\n# Runs: {runs}\n# Experiment: {experiment}" + + @staticmethod + def estimate_run_time(runs, experiment, estimate): + return len(runs) * 10 # Dummy implementation + +# Example usage +if __name__ == "__main__": + def on_property_changed(name): + print(f"Property {name} changed") + + def on_static_property_changed(name): + print(f"Static property {name} changed") + + instrument_info = InstrumentInfo() + instrument_info.property_changed.append(on_property_changed) + InstrumentInfo.static_property_changed.append(on_static_property_changed) + + instrument_info.a2_label = "New A2 Setting:" + instrument_info.a2_choices = ["XLarge", "Large", "Medium", "Small"] + instrument_info.a2_trans_enabled = False + instrument_info.collection_mode_enabled = True \ No newline at end of file diff --git a/Models/RunOrder.cs b/Models/RunOrder.cs new file mode 100644 index 0000000..8aba130 --- /dev/null +++ b/Models/RunOrder.cs @@ -0,0 +1,25 @@ +using System.Collections.ObjectModel; + +namespace LOQ_Script_Gui +{ + class RunOrder + { + public readonly static string AllTrans = "All TRANS first"; + public readonly static string TransFirst = "Alternate - TRANS first"; + public readonly static string AllSans = "All SANS first"; + public readonly static string SansFirst = "Alternate - SANS first"; + + static ObservableCollection choices = new ObservableCollection() + { + AllTrans, TransFirst, AllSans, SansFirst + }; + + public static ObservableCollection Choices + { + get + { + return choices; + } + } + } +} diff --git a/Models/RunOrder.py b/Models/RunOrder.py new file mode 100644 index 0000000..73e5815 --- /dev/null +++ b/Models/RunOrder.py @@ -0,0 +1,20 @@ +class RunOrder: + ALL_TRANS = "All TRANS first" + TRANS_FIRST = "Alternate - TRANS first" + ALL_SANS = "All SANS first" + SANS_FIRST = "Alternate - SANS first" + + choices = [ + ALL_TRANS, + TRANS_FIRST, + ALL_SANS, + SANS_FIRST + ] + + @staticmethod + def get_choices(): + return RunOrder.choices + +# Example usage +if __name__ == "__main__": + print(RunOrder.get_choices()) \ No newline at end of file diff --git a/Models/WaitForUnits.cs b/Models/WaitForUnits.cs new file mode 100644 index 0000000..0484abf --- /dev/null +++ b/Models/WaitForUnits.cs @@ -0,0 +1,25 @@ +using System.Collections.ObjectModel; + +namespace LOQ_Script_Gui +{ + class WaitForUnits + { + public readonly static string MicroAmps = "Microamp-hours"; + public readonly static string Minutes = "Minutes"; + public readonly static string Seconds = "Seconds"; + public readonly static string Frames = "Frames"; + + static ObservableCollection choices = new ObservableCollection() + { + MicroAmps, Minutes, Seconds, Frames + }; + + public static ObservableCollection Choices + { + get + { + return choices; + } + } + } +} diff --git a/Models/WaitForUnits.py b/Models/WaitForUnits.py new file mode 100644 index 0000000..0df9622 --- /dev/null +++ b/Models/WaitForUnits.py @@ -0,0 +1,20 @@ +class WaitForUnits: + MICROAMPS = "Microamp-hours" + MINUTES = "Minutes" + SECONDS = "Seconds" + FRAMES = "Frames" + + choices = [ + MICROAMPS, + MINUTES, + SECONDS, + FRAMES + ] + + @staticmethod + def get_choices(): + return WaitForUnits.choices + +# Example usage +if __name__ == "__main__": + print(WaitForUnits.get_choices()) \ No newline at end of file diff --git a/Properties/AssemblyInfo.cs b/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..1f2339a --- /dev/null +++ b/Properties/AssemblyInfo.cs @@ -0,0 +1,55 @@ +using System.Reflection; +using System.Resources; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Windows; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("LOQ_Script_Gui")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("LOQ_Script_Gui")] +[assembly: AssemblyCopyright("Copyright © 2015")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +//In order to begin building localizable applications, set +//CultureYouAreCodingWith in your .csproj file +//inside a . For example, if you are using US english +//in your source files, set the to en-US. Then uncomment +//the NeutralResourceLanguage attribute below. Update the "en-US" in +//the line below to match the UICulture setting in the project file. + +//[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)] + + +[assembly: ThemeInfo( + ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located + //(used if a resource is not found in the page, + // or application resource dictionaries) + ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located + //(used if a resource is not found in the page, + // app, or any theme specific resource dictionaries) +)] + + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/Properties/AssemblyInfo.py b/Properties/AssemblyInfo.py new file mode 100644 index 0000000..223c844 --- /dev/null +++ b/Properties/AssemblyInfo.py @@ -0,0 +1,23 @@ +from setuptools import setup, find_packages + +setup( + name="LOQ_Script_Gui", + version="1.0.0", + description="", + author="", + author_email="", + url="", + packages=find_packages(), + include_package_data=True, + install_requires=[ + # Add your dependencies here + ], + classifiers=[ + "Programming Language :: Python :: 3", + "License :: OSI Approved :: MIT License", + "Operating System :: OS Independent", + ], + python_requires='>=3.6', +) + +# Additional metadata for localization and resource management can be added as needed. \ No newline at end of file diff --git a/Properties/Resources.Designer.cs b/Properties/Resources.Designer.cs new file mode 100644 index 0000000..2b67d17 --- /dev/null +++ b/Properties/Resources.Designer.cs @@ -0,0 +1,63 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace LOQ_Script_Gui.Properties { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Resources { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Resources() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("LOQ_Script_Gui.Properties.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + } +} diff --git a/Properties/Resources.Designer.py b/Properties/Resources.Designer.py new file mode 100644 index 0000000..d4614f7 --- /dev/null +++ b/Properties/Resources.Designer.py @@ -0,0 +1,31 @@ +import gettext +import os + +class Resources: + def __init__(self): + self.resource_man = None + self.resource_culture = None + + @property + def resource_manager(self): + if self.resource_man is None: + locale_dir = os.path.join(os.path.dirname(__file__), 'locale') + self.resource_man = gettext.translation('resources', localedir=locale_dir, languages=[self.resource_culture]) + return self.resource_man + + @property + def culture(self): + return self.resource_culture + + @culture.setter + def culture(self, value): + self.resource_culture = value + self.resource_man = None # Reset the resource manager to reload with the new culture + +# Example usage +resources = Resources() +resources.culture = 'en_US' # Set the desired culture +_ = resources.resource_manager.gettext + +# Example of using the localized string +print(_("Hello, World!")) \ No newline at end of file diff --git a/Properties/Resources.ini b/Properties/Resources.ini new file mode 100644 index 0000000..0d87d37 --- /dev/null +++ b/Properties/Resources.ini @@ -0,0 +1,16 @@ +#using configparser +[resheader] +resmimetype = text/microsoft-resx +version = 2.0 +reader = System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 +writer = System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + +[data] +Name1 = this is my long string +Color1 = Blue +Bitmap1 = [base64 mime encoded serialized .NET Framework object] +Icon1 = [base64 mime encoded string representing a byte array form of the .NET Framework object] + +[metadata] +Name1.comment = this is a comment +Icon1.comment = This is a comment \ No newline at end of file diff --git a/Properties/Resources.resx b/Properties/Resources.resx new file mode 100644 index 0000000..af7dbeb --- /dev/null +++ b/Properties/Resources.resx @@ -0,0 +1,117 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/Properties/Settings.Designer.cs b/Properties/Settings.Designer.cs new file mode 100644 index 0000000..5f01f51 --- /dev/null +++ b/Properties/Settings.Designer.cs @@ -0,0 +1,158 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace LOQ_Script_Gui.Properties { + + + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "17.0.3.0")] + internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { + + private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); + + public static Settings Default { + get { + return defaultInstance; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("LARMOR")] + public string LastInstrument { + get { + return ((string)(this["LastInstrument"])); + } + set { + this["LastInstrument"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("11")] + public string SampleHeight { + get { + return ((string)(this["SampleHeight"])); + } + set { + this["SampleHeight"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("11")] + public string SampleWidth { + get { + return ((string)(this["SampleWidth"])); + } + set { + this["SampleWidth"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("Flat Plate")] + public string Geometry { + get { + return ((string)(this["Geometry"])); + } + set { + this["Geometry"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("Alternate - TRANS first")] + public string RunOrder { + get { + return ((string)(this["RunOrder"])); + } + set { + this["RunOrder"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("1")] + public string NumTrans { + get { + return ((string)(this["NumTrans"])); + } + set { + this["NumTrans"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("1")] + public string NumSans { + get { + return ((string)(this["NumSans"])); + } + set { + this["NumSans"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("False")] + public string LoopPerRun { + get { + return ((string)(this["LoopPerRun"])); + } + set { + this["LoopPerRun"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("Large = 25, 16 mm")] + public string A2SettingSans { + get { + return ((string)(this["A2SettingSans"])); + } + set { + this["A2SettingSans"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("Large = 25, 16 mm")] + public string A2SettingTrans { + get { + return ((string)(this["A2SettingTrans"])); + } + set { + this["A2SettingTrans"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("Events")] + public string CollectionMode { + get { + return ((string)(this["CollectionMode"])); + } + set { + this["CollectionMode"] = value; + } + } + } +} diff --git a/Properties/Settings.ini b/Properties/Settings.ini new file mode 100644 index 0000000..76b9093 --- /dev/null +++ b/Properties/Settings.ini @@ -0,0 +1,13 @@ +[DEFAULT] +lastinstrument = LOQ +sampleheight = '11' +samplewidth = '11' +geometry = 'Flat Plate' +runorder = 'Alternate - TRANS first' +numtrans = '1' +numsans = '1' +loopperrun = 'False' +a2settingsans = +a2settingtrans = +collectionmode = 'Events' + diff --git a/Properties/Settings.py b/Properties/Settings.py new file mode 100644 index 0000000..6d9f60f --- /dev/null +++ b/Properties/Settings.py @@ -0,0 +1,119 @@ +import configparser +import os + +class Settings: + def __init__(self, filename='Settings.ini'): + self.dict_settings = configparser.ConfigParser() + directory=os.path.dirname(os.path.abspath(__file__)) + self.setting_ini_path = os.path.join(directory,filename) + + self.dict_settings.read(self.setting_ini_path) + + @property + def last_instrument(self): + return self.dict_settings.get('DEFAULT','LastInstrument') + + @last_instrument.setter + def last_instrument(self, value): + self.dict_settings.set('DEFAULT','LastInstrument', str(value)) + self.save() + + @property + def sample_height(self): + return self.dict_settings.get('DEFAULT','SampleHeight') + + @sample_height.setter + def sample_height(self, value): + self.dict_settings.set('DEFAULT','SampleHeight', str(value)) + self.save() + + @property + def sample_width(self): + return self.dict_settings.get('DEFAULT','SampleWidth') + + @sample_width.setter + def sample_width(self, value): + self.dict_settings.set('DEFAULT','SampleWidth', str(value)) + self.save() + + @property + def geometry(self): + return self.dict_settings.get('DEFAULT','Geometry') + + @geometry.setter + def geometry(self, value): + self.dict_settings.set('DEFAULT','Geometry', str(value)) + self.save() + + @property + def run_order(self): + return self.dict_settings.get('DEFAULT','RunOrder') + + @run_order.setter + def run_order(self, value): + self.dict_settings.set('DEFAULT','RunOrder', str(value)) + self.save() + + @property + def num_trans(self): + return self.dict_settings.get('DEFAULT','NumTrans') + + @num_trans.setter + def num_trans(self, value): + self.dict_settings.set('DEFAULT','NumTrans', str(value)) + self.save() + + @property + def num_sans(self): + return self.dict_settings.get('DEFAULT','NumSans') + + @num_sans.setter + def num_sans(self, value): + self.dict_settings.set('DEFAULT','NumSans', str(value)) + self.save() + + @property + def loop_per_run(self): + return self.dict_settings.get('DEFAULT','LoopPerRun') + + @loop_per_run.setter + def loop_per_run(self, value): + self.dict_settings.set('DEFAULT','LoopPerRun', str(value)) + self.save() + + @property + def a2_setting_sans(self): + return self.dict_settings.get('DEFAULT','A2SettingSans') + + @a2_setting_sans.setter + def a2_setting_sans(self, value): + self.dict_settings.set('DEFAULT','A2SettingSans', str(value)) + self.save() + + @property + def a2_setting_trans(self): + return self.dict_settings.get('DEFAULT','A2SettingTrans') + + @a2_setting_trans.setter + def a2_setting_trans(self, value): + self.dict_settings.set('DEFAULT','A2SettingTrans', str(value)) + self.save() + + @property + def collection_mode(self): + return self.dict_settings.get('DEFAULT','CollectionMode') + + @collection_mode.setter + def collection_mode(self, value): + self.dict_settings.set('DEFAULT','CollectionMode', str(value)) + self.save() + + def save(self): + with open(self.setting_ini_path, 'w') as configfile: + self.dict_settings.write(configfile) + +# Example usage +settings = Settings() +print(settings.last_instrument) +settings.last_instrument = 'LOQ' +print(settings.last_instrument) \ No newline at end of file diff --git a/Properties/Settings.settings b/Properties/Settings.settings new file mode 100644 index 0000000..27069e6 --- /dev/null +++ b/Properties/Settings.settings @@ -0,0 +1,39 @@ + + + + + + LARMOR + + + 11 + + + 11 + + + Flat Plate + + + Alternate - TRANS first + + + 1 + + + 1 + + + False + + + Large = 25, 16 mm + + + Large = 25, 16 mm + + + Events + + + \ No newline at end of file diff --git a/SANS_Icon.ico b/SANS_Icon.ico new file mode 100644 index 0000000000000000000000000000000000000000..514a7d8425efc1d81ec678aafd00095f5cbc87dc GIT binary patch literal 59390 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 Y`E%t*c(~Jru`u5oWi6#@1 + + + + Debug + AnyCPU + {5DBFF2C3-05A0-4B4B-9080-8224D86A7B40} + WinExe + Properties + LOQ_Script_Gui + SANS_Script_Gui_v4 + v4.8 + 512 + {60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + 4 + false + + publish\ + true + Disk + false + Foreground + 7 + Days + false + false + true + 0 + 1.0.0.%2a + false + true + + + x64 + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + false + + + x64 + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + SANS_Icon.ico + + + + True + + + + + + + + + + + 4.0 + + + + + + + + MSBuild:Compile + Designer + + + + + + PreviewDialog.xaml + + + + + + + + EstimationPanel.xaml + + + + + + + + + SettingsPanel.xaml + + + + + + + Designer + MSBuild:Compile + + + Designer + MSBuild:Compile + + + MSBuild:Compile + Designer + + + App.xaml + Code + + + MainWindow.xaml + Code + + + Designer + MSBuild:Compile + + + + + Code + + + True + True + Resources.resx + + + True + Settings.settings + True + + + ResXFileCodeGenerator + Resources.Designer.cs + + + SettingsSingleFileGenerator + Settings.Designer.cs + + + + + + + + + False + Microsoft .NET Framework 4.5 %28x86 and x64%29 + true + + + False + .NET Framework 3.5 SP1 + false + + + + + + + + + + + \ No newline at end of file diff --git a/SANS_Script_GUI_v4.csproj.py b/SANS_Script_GUI_v4.csproj.py new file mode 100644 index 0000000..10d6092 --- /dev/null +++ b/SANS_Script_GUI_v4.csproj.py @@ -0,0 +1,175 @@ +import xml.etree.ElementTree as ET + +root = ET.Element("Project", ToolsVersion="12.0", DefaultTargets="Build", xmlns="http://schemas.microsoft.com/developer/msbuild/2003") + +ET.SubElement(root, "Import", Project="$(MSBuildExtensionsPath)\\$(MSBuildToolsVersion)\\Microsoft.Common.props", Condition="Exists('$(MSBuildExtensionsPath)\\$(MSBuildToolsVersion)\\Microsoft.Common.props')") + +property_group = ET.SubElement(root, "PropertyGroup") +ET.SubElement(property_group, "Configuration", Condition=" '$(Configuration)' == '' ").text = "Debug" +ET.SubElement(property_group, "Platform", Condition=" '$(Platform)' == '' ").text = "AnyCPU" +ET.SubElement(property_group, "ProjectGuid").text = "{5DBFF2C3-05A0-4B4B-9080-8224D86A7B40}" +ET.SubElement(property_group, "OutputType").text = "WinExe" +ET.SubElement(property_group, "AppDesignerFolder").text = "Properties" +ET.SubElement(property_group, "RootNamespace").text = "LOQ_Script_Gui" +ET.SubElement(property_group, "AssemblyName").text = "SANS_Script_Gui_v4" +ET.SubElement(property_group, "TargetFrameworkVersion").text = "v4.8" +ET.SubElement(property_group, "FileAlignment").text = "512" +ET.SubElement(property_group, "ProjectTypeGuids").text = "{60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}" +ET.SubElement(property_group, "WarningLevel").text = "4" +ET.SubElement(property_group, "IsWebBootstrapper").text = "false" +ET.SubElement(property_group, "TargetFrameworkProfile") +ET.SubElement(property_group, "PublishUrl").text = "publish\\" +ET.SubElement(property_group, "Install").text = "true" +ET.SubElement(property_group, "InstallFrom").text = "Disk" +ET.SubElement(property_group, "UpdateEnabled").text = "false" +ET.SubElement(property_group, "UpdateMode").text = "Foreground" +ET.SubElement(property_group, "UpdateInterval").text = "7" +ET.SubElement(property_group, "UpdateIntervalUnits").text = "Days" +ET.SubElement(property_group, "UpdatePeriodically").text = "false" +ET.SubElement(property_group, "UpdateRequired").text = "false" +ET.SubElement(property_group, "MapFileExtensions").text = "true" +ET.SubElement(property_group, "ApplicationRevision").text = "0" +ET.SubElement(property_group, "ApplicationVersion").text = "1.0.0.%2a" +ET.SubElement(property_group, "UseApplicationTrust").text = "false" +ET.SubElement(property_group, "BootstrapperEnabled").text = "true" + +debug_property_group = ET.SubElement(root, "PropertyGroup", Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ") +ET.SubElement(debug_property_group, "PlatformTarget").text = "x64" +ET.SubElement(debug_property_group, "DebugSymbols").text = "true" +ET.SubElement(debug_property_group, "DebugType").text = "full" +ET.SubElement(debug_property_group, "Optimize").text = "false" +ET.SubElement(debug_property_group, "OutputPath").text = "bin\\Debug\\" +ET.SubElement(debug_property_group, "DefineConstants").text = "DEBUG;TRACE" +ET.SubElement(debug_property_group, "ErrorReport").text = "prompt" +ET.SubElement(debug_property_group, "WarningLevel").text = "4" +ET.SubElement(debug_property_group, "Prefer32Bit").text = "false" + +release_property_group = ET.SubElement(root, "PropertyGroup", Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ") +ET.SubElement(release_property_group, "PlatformTarget").text = "x64" +ET.SubElement(release_property_group, "DebugType").text = "pdbonly" +ET.SubElement(release_property_group, "Optimize").text = "true" +ET.SubElement(release_property_group, "OutputPath").text = "bin\\Release\\" +ET.SubElement(release_property_group, "DefineConstants").text = "TRACE" +ET.SubElement(release_property_group, "ErrorReport").text = "prompt" +ET.SubElement(release_property_group, "WarningLevel").text = "4" + +icon_property_group = ET.SubElement(root, "PropertyGroup") +ET.SubElement(icon_property_group, "ApplicationIcon").text = "SANS_Icon.ico" + +references = [ + ("Microsoft.Office.Interop.Excel, Version=15.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c, processorArchitecture=MSIL", "True"), + "ReachFramework", + "System", + "System.Data", + "System.Xml", + "Microsoft.CSharp", + "System.Core", + "System.Xml.Linq", + "System.Data.DataSetExtensions", + ("System.Xaml", "4.0"), + "WindowsBase", + "PresentationCore", + "PresentationFramework" +] + +item_group = ET.SubElement(root, "ItemGroup") +for ref in references: + if isinstance(ref, tuple): + reference = ET.SubElement(item_group, "Reference", Include=ref[0]) + ET.SubElement(reference, "EmbedInteropTypes").text = ref[1] + else: + ET.SubElement(item_group, "Reference", Include=ref) + +compile_files = [ + "Models\\DataCollection.py", + "Models\\InstrumentInfo.py", + "IO\\CsvExporter.py", + "Dialogs\\PreviewDialog.xaml.py", + "IO\\ExcelIO.py", + "Models\\A2Setting.py", + "Models\\Estimation.py", + "Models\\Geometry.py", + "Models\\RunOrder.py", + "EstimationPanel.xaml.py", + "Models\\Experiment.py", + "Models\\ExperimentSettings.py", + "Models\\WaitForUnits.py", + "Scripting\\GeniePythonScriptGenerator.py", + "Scripting\\IScriptGenerator.py", + "Scripting\\OpenGenieScriptGenerator.py", + "SettingsPanel.xaml.py", + "ViewModels\\DataGridVM.py", + "ViewModels\\EstimationPanelVM.py", + "ViewModels\\InstrumentVM.py", + "ViewModels\\SettingsPanelVM.py", + "App.xaml.py", + "MainWindow.xaml.py", + "Properties\\AssemblyInfo.py", + "Properties\\Resources.Designer.py", + "Properties\\Settings.Designer.py" +] + +for file in compile_files: + compile = ET.SubElement(item_group, "Compile", Include=file) + if file.endswith(".xaml.py"): + ET.SubElement(compile, "DependentUpon").text = file.replace(".py", "") + elif file.endswith(".Designer.py"): + ET.SubElement(compile, "AutoGen").text = "True" + ET.SubElement(compile, "DesignTime").text = "True" + ET.SubElement(compile, "DependentUpon").text = file.replace(".Designer.py", ".resx") + if "Settings" in file: + ET.SubElement(compile, "DesignTimeSharedInput").text = "True" + +pages = [ + "Dialogs\\PreviewDialog.xaml", + "EstimationPanel.xaml", + "MainWindow.xaml", + "SettingsPanel.xaml" +] + +for page in pages: + page_element = ET.SubElement(item_group, "Page", Include=page) + ET.SubElement(page_element, "SubType").text = "Designer" + ET.SubElement(page_element, "Generator").text = "MSBuild:Compile" + +embedded_resources = [ + "Properties\\Resources.resx" +] + +for resource in embedded_resources: + embedded_resource = ET.SubElement(item_group, "EmbeddedResource", Include=resource) + ET.SubElement(embedded_resource, "Generator").text = "ResXFileCodeGenerator" + ET.SubElement(embedded_resource, "LastGenOutput").text = resource.replace(".resx", ".Designer.py") + +none_files = [ + "Properties\\Settings.settings", + "App.config" +] + +for none_file in none_files: + none = ET.SubElement(item_group, "None", Include=none_file) + if none_file.endswith(".settings"): + ET.SubElement(none, "Generator").text = "SettingsSingleFileGenerator" + ET.SubElement(none, "LastGenOutput").text = none_file.replace(".settings", ".Designer.py") + +ET.SubElement(item_group, "AppDesigner", Include="Properties\\") + +bootstrapper_packages = [ + (".NETFramework,Version=v4.5", "Microsoft .NET Framework 4.5 %28x86 and x64%29", "true"), + ("Microsoft.Net.Framework.3.5.SP1", ".NET Framework 3.5 SP1", "false") +] + +for package in bootstrapper_packages: + bootstrapper_package = ET.SubElement(item_group, "BootstrapperPackage", Include=package[0]) + ET.SubElement(bootstrapper_package, "Visible").text = "False" + ET.SubElement(bootstrapper_package, "ProductName").text = package[1] + ET.SubElement(bootstrapper_package, "Install").text = package[2] + +ET.SubElement(item_group, "Resource", Include="SANS_Icon.ico") + +ET.SubElement(item_group, "WCFMetadata", Include="Connected Services\\") + +ET.SubElement(root, "Import", Project="$(MSBuildToolsPath)\\Microsoft.CSharp.targets") + +tree = ET.ElementTree(root) +tree.write("SANS_Script_GUI_v4.xml", encoding="utf-8", xml_declaration=True) \ No newline at end of file diff --git a/SANS_Script_GUI_v4.csproj.user.py b/SANS_Script_GUI_v4.csproj.user.py new file mode 100644 index 0000000..13474b5 --- /dev/null +++ b/SANS_Script_GUI_v4.csproj.user.py @@ -0,0 +1,16 @@ +import xml.etree.ElementTree as ET + +root = ET.Element("Project", ToolsVersion="14.0", xmlns="http://schemas.microsoft.com/developer/msbuild/2003") + +property_group = ET.SubElement(root, "PropertyGroup") +ET.SubElement(property_group, "PublishUrlHistory").text = "publish\\" +ET.SubElement(property_group, "InstallUrlHistory") +ET.SubElement(property_group, "SupportUrlHistory") +ET.SubElement(property_group, "UpdateUrlHistory") +ET.SubElement(property_group, "BootstrapperUrlHistory") +ET.SubElement(property_group, "ErrorReportUrlHistory") +ET.SubElement(property_group, "FallbackCulture").text = "en-US" +ET.SubElement(property_group, "VerifyUploadedFiles").text = "false" + +tree = ET.ElementTree(root) +tree.write("SANS_Script_GUI_v4.xml", encoding="utf-8", xml_declaration=True) \ No newline at end of file diff --git a/SANS_Script_GUI_v4.xml b/SANS_Script_GUI_v4.xml new file mode 100644 index 0000000..5e50bb7 --- /dev/null +++ b/SANS_Script_GUI_v4.xml @@ -0,0 +1,2 @@ + +DebugAnyCPU{5DBFF2C3-05A0-4B4B-9080-8224D86A7B40}WinExePropertiesLOQ_Script_GuiSANS_Script_Gui_v4v4.8512{60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}4falsepublish\trueDiskfalseForeground7Daysfalsefalsetrue01.0.0.%2afalsetruex64truefullfalsebin\Debug\DEBUG;TRACEprompt4falsex64pdbonlytruebin\Release\TRACEprompt4SANS_Icon.icoTrue4.0Dialogs\PreviewDialog.xamlEstimationPanel.xamlSettingsPanel.xamlApp.xamlMainWindow.xamlTrueTrueProperties\Resources.resxTrueTrueProperties\Settings.resxTrueDesignerMSBuild:CompileDesignerMSBuild:CompileDesignerMSBuild:CompileDesignerMSBuild:CompileResXFileCodeGeneratorProperties\Resources.Designer.csSettingsSingleFileGeneratorProperties\Settings.Designer.csFalseMicrosoft .NET Framework 4.5 %28x86 and x64%29trueFalse.NET Framework 3.5 SP1false \ No newline at end of file diff --git a/SANS_Script_Writer/SANS_Script_Writer.sln b/SANS_Script_Writer/SANS_Script_Writer.sln new file mode 100644 index 0000000..d5ff645 --- /dev/null +++ b/SANS_Script_Writer/SANS_Script_Writer.sln @@ -0,0 +1,20 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.12.35527.113 d17.12 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{888888A0-9F3D-457C-B088-3A5042F75D52}") = "SANS_Script_Writer", "SANS_Script_Writer\SANS_Script_Writer.pyproj", "{83C517F7-94B7-490E-9812-1A281BC3AABB}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {83C517F7-94B7-490E-9812-1A281BC3AABB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {83C517F7-94B7-490E-9812-1A281BC3AABB}.Release|Any CPU.ActiveCfg = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/SANS_Script_Writer/SANS_Script_Writer/SANS_Script_Writer.py b/SANS_Script_Writer/SANS_Script_Writer/SANS_Script_Writer.py new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/SANS_Script_Writer/SANS_Script_Writer/SANS_Script_Writer.py @@ -0,0 +1 @@ + diff --git a/SANS_Script_Writer/SANS_Script_Writer/SANS_Script_Writer.pyproj b/SANS_Script_Writer/SANS_Script_Writer/SANS_Script_Writer.pyproj new file mode 100644 index 0000000..b77f107 --- /dev/null +++ b/SANS_Script_Writer/SANS_Script_Writer/SANS_Script_Writer.pyproj @@ -0,0 +1,35 @@ + + + Debug + 2.0 + 83c517f7-94b7-490e-9812-1a281bc3aabb + . + SANS_Script_Writer.py + + + . + . + SANS_Script_Writer + SANS_Script_Writer + + + true + false + + + true + false + + + + + + + + + + + + \ No newline at end of file diff --git a/Scripting/GeniePythonScriptGenerator.cs b/Scripting/GeniePythonScriptGenerator.cs new file mode 100644 index 0000000..3db9a71 --- /dev/null +++ b/Scripting/GeniePythonScriptGenerator.cs @@ -0,0 +1,540 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Collections.ObjectModel; + +namespace LOQ_Script_Gui.Scripting +{ + class GeniePythonScriptGenerator : IScriptGenerator + { + string indent = " "; + + private bool isLarmor = false; + private bool isloq = false; + private bool issans2d = false; + private bool iszoom = false; + + public GeniePythonScriptGenerator(bool isLarmor, bool isloq, bool isZoom, bool isSans2d) + { + this.isLarmor = isLarmor; + this.isloq = isloq; + this.issans2d = isSans2d; + this.iszoom = isZoom; + } + + public string GenerateScript(ObservableCollection runs, ExperimentSettings settings, String scriptname) + { + string script = "# Script created by SANSScript at " + DateTime.Now + Environment.NewLine; + script += Environment.NewLine; + script += GenerateHeader(scriptname); + script += GenerateSetup(settings); + script += GenerateRuns(runs, settings); + script += Environment.NewLine; + + return script; + } + + private string GetIndentString(int n) + { + return new StringBuilder().Insert(0, indent, n).ToString(); + } + + private string GenerateHeader(String scriptname) + { + //string script = "import LSS.SANSroutines_v2 as lm" + Environment.NewLine ; + //string script = "from SansScripting import *" + Environment.NewLine; + //script += "@user_script" + Environment.NewLine + Environment.NewLine; + //script += Environment.NewLine; + string script = "import sys" + Environment.NewLine; + script += "from genie_python import genie as g, BLOCK_NAMES as b" + Environment.NewLine; + script += "sys.path.append(r\"c:\\instrument\\scripts\")" + Environment.NewLine; + + if (isLarmor) + { + script += "from instrument.larmor import * # pylint: disable=wildcard-import, unused-wildcard-import" + Environment.NewLine; + } + if (isloq) + { + script += "from instrument.loq import * # pylint: disable=wildcard-import, unused-wildcard-import" + Environment.NewLine; + } + if (iszoom) + { + script += "from instrument.zoom import * # pylint: disable=wildcard-import, unused-wildcard-import" + Environment.NewLine; + } + if (issans2d) + { + script += "from instrument.sans2d import * # pylint: disable=wildcard-import, unused-wildcard-import" + Environment.NewLine; + } + + script += Environment.NewLine; + script += "def "+scriptname+"():" + Environment.NewLine; + + return script; + } + + private string GenerateSetup(ExperimentSettings settings) + { + string script = ""; + + // TODO: Set number of available periods + + //script += GetIndentString(1) + "lm.setuplarmor_normal()" + Environment.NewLine; + script += GetIndentString(1) + string.Format("g.change_sample_par('Width', '{0}')", settings.SampleWidth) + Environment.NewLine; + script += GetIndentString(1) + string.Format("g.change_sample_par('height', '{0}')", settings.SampleHeight) + Environment.NewLine; + script += GetIndentString(1) + string.Format("g.change_sample_par('Geometry', '{0}')", settings.SampleGeometry) + Environment.NewLine + Environment.NewLine; + //script += GetIndentString(1) + string.Format("set_pv('IN:LARMOR:PARS:SAMPLE:WIDTH:SP', '{0}')", settings.SampleWidth) + Environment.NewLine; + //script += GetIndentString(1) + string.Format("set_pv('IN:LARMOR:PARS:SAMPLE:HEIGHT:SP', '{0}')", settings.SampleHeight) + Environment.NewLine; + //script += GetIndentString(1) + string.Format("set_pv('IN:LARMOR:PARS:SAMPLE:GEOMETRY:SP', '{0}')", settings.SampleGeometry) + Environment.NewLine + Environment.NewLine; + + return script; + } + + private string CheckPositions(ObservableCollection runs) + { + string script = ""; + + // Check that the positions are vailid + if (isLarmor) + { + string positionlist = "\""; + + foreach (Experiment exp in runs) + { + if (!string.IsNullOrWhiteSpace(exp.Position)) + { + positionlist += exp.Position + ","; + } + } + + if (positionlist.Length > 1) + { + if (positionlist.EndsWith(",")) + { + positionlist = positionlist.Substring(0, positionlist.Length - 1); + } + + positionlist += "\""; + + string tscript = string.Format("if check_move_pos([{0}]) != True", positionlist); + tscript = tscript.Replace(",", "\",\""); + script += GetIndentString(1) + tscript; + script += Environment.NewLine; + script += GetIndentString(2)+"print \"Invalid positions - script aborted\"" + Environment.NewLine; + script += GetIndentString(2)+"return" + Environment.NewLine + Environment.NewLine; + } + } + + //return script; + return ""; + } + private string GenerateRuns(ObservableCollection runs, ExperimentSettings settings) + { + string script = ""; + script = CheckPositions(runs); + + if (runs.Count == 0) + { + // Nothing to do + return script; + } + + if (settings.Order == RunOrder.AllTrans) + { + // All TRANS first + script += GetIndentString(1) + string.Format("for i in range({0}):", settings.NumTrans) + Environment.NewLine; + + foreach (Experiment exp in runs) + { + if (exp.Trans != 0) + { + int indent = 2; + script = DoTransRun(script, exp, indent, settings); + } + } + + // Then the SANS + script += GetIndentString(1) + string.Format("for i in range({0}):", settings.NumSans) + Environment.NewLine; + + foreach (Experiment exp in runs) + { + if (exp.Sans != 0) + { + int indent = 2; + script = DoSansRun(script, exp, indent, settings); + } + } + } + else if (settings.Order == RunOrder.AllSans) + { + // All SANS first + script += GetIndentString(1) + string.Format("for i in range({0}):", settings.NumSans) + Environment.NewLine; + + foreach (Experiment exp in runs) + { + if (exp.Sans != 0) + { + int indent = 2; + script = DoSansRun(script, exp, indent, settings); + } + } + + // Then the TRANS if we are running them + // check to see if there are actually any trans runs required by adding up all the trans count times + double sumTrans = 0.0; + foreach (Experiment exp in runs) + { + if (exp.Trans != 0) + { + sumTrans += exp.Trans; + } + } + + if (settings.NumTrans != 0 && sumTrans > 0.01) + { + script += GetIndentString(1) + string.Format("for i in range({0}):", settings.NumTrans) + Environment.NewLine; + } + foreach (Experiment exp in runs) + { + if (exp.Trans != 0) + { + int indent = 2; + script = DoTransRun(script, exp, indent, settings); + } + } + } + else if (settings.Order == RunOrder.TransFirst || settings.Order == RunOrder.SansFirst) + { + // TRANS - SANS - TRANS - SANS etc. or SANS - TRANS - SANS - TRANS etc + // Note: number of SANS and TRANS runs may be different + script += GetIndentString(1) + string.Format("num_sans = {0}", settings.NumSans) + Environment.NewLine; + script += GetIndentString(1) + string.Format("num_trans = {0}", settings.NumTrans) + Environment.NewLine; + script += GetIndentString(1) + "count = 0" + Environment.NewLine; + script += GetIndentString(1) + "while True:" + Environment.NewLine; + + int indent = 2; + + foreach (Experiment exp in runs) + { + if (settings.Order == RunOrder.TransFirst) + { + if (exp.Trans != 0) + { + script += GetIndentString(indent) + "if count < num_trans:" + Environment.NewLine; + script = DoTransRun(script, exp, indent + 1, settings); + } + if (exp.Sans != 0) + { + script += GetIndentString(indent) + "if count < num_sans:" + Environment.NewLine; + script = DoSansRun(script, exp, indent + 1, settings); + } + } + else + { + if (exp.Sans != 0) + { + script += GetIndentString(indent) + "if count < num_sans:" + Environment.NewLine; + script = DoSansRun(script, exp, indent + 1, settings); + } + if (exp.Trans != 0) + { + script += GetIndentString(indent) + "if count < num_trans:" + Environment.NewLine; + script = DoTransRun(script, exp, indent + 1, settings); + } + } + } + + script += GetIndentString(indent) + "count += 1" + Environment.NewLine; + script += GetIndentString(indent) + "if count >= num_trans and count >= num_sans: break" + Environment.NewLine; + } + + return script; + } + + private string DoTransRun(string script, Experiment exp, int indent, ExperimentSettings settings) + { + script += DoSampleEnvironment(exp, indent); + //script += SetAperture(settings.A2SettingTrans, indent); + + string waitfor = "uamps"; // Default to uamps + string rtype = "histogram"; // Default to event mode + string fulltitle = "{" + exp.Sample + "}"; + + if (settings.CollectionMode == DataCollection.Events) + { + rtype = "event"; + } + + if (exp.TransWait == WaitForUnits.Frames) + { + waitfor = "frames"; + } + else if (exp.TransWait == WaitForUnits.Seconds) + { + waitfor = "seconds"; + } + else if (exp.TransWait == WaitForUnits.Minutes) + { + waitfor = "minutes"; + } + + if (isLarmor == true || iszoom == true) + { + script += GetIndentString(indent) + string.Format("do_trans(title='{1}',pos='{0}', {2}={3}, thickness={4})", + exp.Position, fulltitle, waitfor, exp.Trans, exp.Thickness) + Environment.NewLine; + } + else if(isloq == true) + { + script += GetIndentString(indent) + string.Format("do_trans(title='{1}',pos='{0}', {2}={3}, thickness={4}, aperture='small')", + exp.Position, fulltitle, waitfor, exp.Trans, exp.Thickness) + Environment.NewLine; + } + else + { + script += GetIndentString(indent) + string.Format("do_trans(title='{1}',pos='{0}', {2}={3}, thickness={4}, dae='transmission', aperture='{5}')", + exp.Position, fulltitle, waitfor, exp.Trans, exp.Thickness,ApertureString(settings.A2SettingTrans)) + Environment.NewLine; + } + + script += GetIndentString(indent) + exp.PostCommand + Environment.NewLine; + return script; + } + + private string DoSansRun(string script, Experiment exp, int indent, ExperimentSettings settings) + { + script += DoSampleEnvironment(exp, indent); + //script += SetAperture(settings.A2SettingSans, indent); + + string waitfor = "uamps"; // Default to uamps + string rtype = "histogram"; // Default to histogram mode + string fulltitle = "{" + exp.Sample + "}"; + + if (!string.IsNullOrWhiteSpace(exp.Background)) + { + fulltitle += "_{" + exp.Background + "}"; + } + + if (settings.CollectionMode == DataCollection.Events || settings.CollectionMode == DataCollection.TRANSSANS_Events) + { + rtype = "event"; + } + else if (settings.CollectionMode == DataCollection.SESANS) + { + rtype = "sesans"; + } + if (settings.CollectionMode == DataCollection.TRANSSANS_Events || settings.CollectionMode == DataCollection.TRANSSANS_Histogram) + { + // add TRANS to the title so that when SANS is added by do_sans we get _TRANS_SANS + fulltitle += "_TRANS"; + } + + if (exp.SansWait == WaitForUnits.Frames) + { + waitfor = "frames"; + } + else if (exp.SansWait == WaitForUnits.Seconds) + { + waitfor = "seconds"; + } + else if (exp.SansWait == WaitForUnits.Minutes) + { + waitfor = "minutes"; + } + + if (isLarmor == true || iszoom == true) + { + script += GetIndentString(indent) + string.Format("do_sans(title='{1}',pos='{0}', {2}={3}, thickness={4}, dae='{5}')", + exp.Position, fulltitle, waitfor, exp.Sans, exp.Thickness, rtype) + Environment.NewLine; + } + else + { + script += GetIndentString(indent) + string.Format("do_sans(title='{1}',pos='{0}', {2}={3}, thickness={4}, dae='{5}',aperture='{6}')", + exp.Position, fulltitle, waitfor, exp.Sans, exp.Thickness, rtype,ApertureString(settings.A2SettingSans)) + Environment.NewLine; + } + + script += GetIndentString(indent) + exp.PostCommand + Environment.NewLine; + return script; + } + + private string SetAperture(string size, int indent) + { + string script = ""; + if (size == A2Setting.Large || size == A2Setting.Larmor) + { + script += GetIndentString(indent) + "set_aperture('large')" + Environment.NewLine; + } + else if (size == A2Setting.Medium || size == A2Setting.Larmor) + { + script += GetIndentString(indent) + "set_aperture('medium')" + Environment.NewLine; + } + else if (size == A2Setting.Small || size == A2Setting.Larmor) + { + script += GetIndentString(indent) + "set_aperture('small')" + Environment.NewLine; + } + else if (size == A2Setting.XLarge || size == A2Setting.Larmor) + { + script += GetIndentString(indent) + "set_aperture('xlarge')" + Environment.NewLine; + } + return script; + } + + private string ApertureString(string size) + { + string script = ""; + if (size == A2Setting.Large || size == A2Setting.Larmor) + { + script = "large"; + } + else if (size == A2Setting.Medium || size == A2Setting.Larmor) + { + script = "medium"; + } + else if (size == A2Setting.Small || size == A2Setting.Larmor) + { + script = "small"; + } + else if (size == A2Setting.XLarge || size == A2Setting.Larmor) + { + script = "xlarge"; + } + return script; + } + + private string DoSampleEnvironment(Experiment exp, int indent) + { + string script = ""; + + // Set Temps first + if (!string.IsNullOrWhiteSpace(exp.Temperature1)) + { + double temp1 = Convert.ToDouble(exp.Temperature1); + double temp1low = temp1-1.0; + double temp1high = temp1 + 1.0; + if (isloq == true) + { + script += GetIndentString(indent) + string.Format("g.cset(internal_setpoint_1={0})", exp.Temperature1) + Environment.NewLine; + script += GetIndentString(indent) + string.Format("g.cset(\"Julabo_1_ext\",lowlimit={0},highlimit={1},runcontrol=True)", Convert.ToString(temp1low), Convert.ToString(temp1high)) + Environment.NewLine; + }else + { + script += GetIndentString(indent) + string.Format("g.cset(JULABO1_SP={0})", exp.Temperature1) + Environment.NewLine; + script += GetIndentString(indent) + string.Format("g.cset(\"JULABO1_TEMP\",lowlimit={0},highlimit={1},runcontrol=True)", Convert.ToString(temp1low), Convert.ToString(temp1high)) + Environment.NewLine; + } + } + + if (!string.IsNullOrWhiteSpace(exp.Temperature2)) + { + double temp2 = Convert.ToDouble(exp.Temperature2); + double temp2low = temp2 - 1.0; + double temp2high = temp2 + 1.0; + if (isloq == true) + { + script += GetIndentString(indent) + string.Format("g.cset(internal_setpoint_2={0})", exp.Temperature2) + Environment.NewLine; + script += GetIndentString(indent) + string.Format("g.cset(\"Julabo_2_ext\",lowlimit={0},highlimit={1},runcontrol=True)", Convert.ToString(temp2low), Convert.ToString(temp2high)) + Environment.NewLine; + } + else + { + script += GetIndentString(indent) + string.Format("g.cset(JULABO2_SP={0})", exp.Temperature2) + Environment.NewLine; + script += GetIndentString(indent) + string.Format("g.cset(\"JULABO2_TEMP\",lowlimit={0},highlimit={1},runcontrol=True)", Convert.ToString(temp2low), Convert.ToString(temp2high)) + Environment.NewLine; + } + } + + // Check for pre-command - always run immediately after the temp is set + if (!string.IsNullOrWhiteSpace(exp.PreCommand)) + { + script += GetIndentString(indent) + exp.PreCommand + Environment.NewLine; + } + + if (!string.IsNullOrWhiteSpace(exp.Field)) + { + script += GetIndentString(indent) + string.Format("g.cset(FIELD={0})", exp.Field) + Environment.NewLine; + } + + if (!string.IsNullOrWhiteSpace(exp.ShearRate1)) + { + script += GetIndentString(indent) + string.Format("g.cset(SHEAR_RATE_1={0})", exp.ShearRate1) + Environment.NewLine; + } + + if (!string.IsNullOrWhiteSpace(exp.ShearRate2)) + { + script += GetIndentString(indent) + string.Format("g.cset(SHEAR_RATE_2={0})", exp.ShearRate2) + Environment.NewLine; + } + + if (!string.IsNullOrWhiteSpace(exp.ShearAngle1)) + { + script += GetIndentString(indent) + string.Format("g.cset(SHEAR_ANGLE_1={0})", exp.ShearAngle1) + Environment.NewLine; + } + + if (!string.IsNullOrWhiteSpace(exp.ShearAngle2)) + { + script += GetIndentString(indent) + string.Format("g.cset(SHEAR_ANGLE_2={0})", exp.ShearAngle1) + Environment.NewLine; + } + + return script; + } + + public string EstimateRunTime(ObservableCollection runs, ExperimentSettings settings, Estimation estimation) + { + double minutes = 0; + + if (settings.NumSans > 0) + { + minutes += CalculateTime(runs, true, estimation.CountRate, estimation.MoveTime) * settings.NumSans; + } + + if (settings.NumTrans > 0) + { + minutes += CalculateTime(runs, false, estimation.CountRate, estimation.MoveTime) * settings.NumTrans; + } + + DateTime time = new DateTime(1, 1, 1, 0, 0, 0); + + time = time.AddMinutes(minutes); + + return time.ToLongTimeString(); + } + + private static double CalculateTime(ObservableCollection runs, bool isSans, double countRate, double secondsBetween) + { + double minutes = 0; + + foreach (Experiment exp in runs) + { + if (!string.IsNullOrWhiteSpace(exp.Position)) + { + if (isSans) + { + minutes += GetMinutes(exp.SansWait, exp.Sans, countRate); + } + else + { + minutes += GetMinutes(exp.TransWait, exp.Trans, countRate); + } + + minutes += secondsBetween / 60; + } + } + + return minutes; + } + + private static double GetMinutes(string wait, double length, double countRate) + { + if (wait == WaitForUnits.Minutes) + { + return length; + } + else if (wait == WaitForUnits.Seconds) + { + return length / 60.0; + } + else if (wait == WaitForUnits.Frames) + { + return length / (25.0 * 60.0); + } + else + { + // If in doubt use micro-amps + return length / (countRate / 60.0); + } + } + } +} diff --git a/Scripting/GeniePythonScriptGenerator.py b/Scripting/GeniePythonScriptGenerator.py new file mode 100644 index 0000000..51ca3fe --- /dev/null +++ b/Scripting/GeniePythonScriptGenerator.py @@ -0,0 +1,256 @@ +from datetime import datetime, timedelta +from Models.Estimation import Estimation +from Models.Experiment import Experiment +from Models.ExperimentSettings import ExperimentSettings + +class GeniePythonScriptGenerator: + def __init__(self, is_larmor, is_loq, is_zoom, is_sans2d): + self.indent = " " + self.is_larmor = is_larmor + self.is_loq = is_loq + self.is_zoom = is_zoom + self.is_sans2d = is_sans2d + + def generate_script(self, runs, settings, scriptname): + script = f"# Script created by SANSScript at {datetime.now()}\n\n" + script += self.generate_header(scriptname) + script += self.generate_setup(settings) + script += self.generate_runs(runs, settings) + script += "\n" + return script + + def get_indent_string(self, n): + return self.indent * n + + def generate_header(self, scriptname): + script = "import sys\n" + script += "from genie_python import genie as g, BLOCK_NAMES as b\n" + script += "sys.path.append(r\"c:\\instrument\\scripts\")\n" + + if self.is_larmor: + script += "from instrument.larmor import * # pylint: disable=wildcard-import, unused-wildcard-import\n" + if self.is_loq: + script += "from instrument.loq import * # pylint: disable=wildcard-import, unused-wildcard-import\n" + if self.is_zoom: + script += "from instrument.zoom import * # pylint: disable=wildcard-import, unused-wildcard-import\n" + if self.is_sans2d: + script += "from instrument.sans2d import * # pylint: disable=wildcard-import, unused-wildcard-import\n" + + script += f"\ndef {scriptname}():\n" + return script + + def generate_setup(self, settings): + script = "" + script += self.get_indent_string(1) + f"g.change_sample_par('Width', '{settings.sample_width}')\n" + script += self.get_indent_string(1) + f"g.change_sample_par('height', '{settings.sample_height}')\n" + script += self.get_indent_string(1) + f"g.change_sample_par('Geometry', '{settings.sample_geometry}')\n\n" + return script + + def check_positions(self, runs): + script = "" + if self.is_larmor: + position_list = ",".join([exp.position for exp in runs if exp.position]) + if position_list: + script += self.get_indent_string(1) + f"if check_move_pos(['{position_list}']) != True:\n" + script += self.get_indent_string(2) + "print('Invalid positions - script aborted')\n" + script += self.get_indent_string(2) + "return\n\n" + return script + + def generate_runs(self, runs, settings): + script = self.check_positions(runs) + if not runs: + return script + + if settings.order == "AllTrans": + script += self.get_indent_string(1) + f"for i in range({settings.num_trans}):\n" + for exp in runs: + if exp.trans: + script = self.do_trans_run(script, exp, 2, settings) + script += self.get_indent_string(1) + f"for i in range({settings.num_sans}):\n" + for exp in runs: + if exp.sans: + script = self.do_sans_run(script, exp, 2, settings) + elif settings.order == "AllSans": + script += self.get_indent_string(1) + f"for i in range({settings.num_sans}):\n" + for exp in runs: + if exp.sans: + script = self.do_sans_run(script, exp, 2, settings) + if settings.num_trans and any(exp.trans for exp in runs): + script += self.get_indent_string(1) + f"for i in range({settings.num_trans}):\n" + for exp in runs: + if exp.trans: + script = self.do_trans_run(script, exp, 2, settings) + else: + script += self.get_indent_string(1) + f"num_sans = {settings.num_sans}\n" + script += self.get_indent_string(1) + f"num_trans = {settings.num_trans}\n" + script += self.get_indent_string(1) + "count = 0\n" + script += self.get_indent_string(1) + "while True:\n" + indent = 2 + for exp in runs: + if settings.order == "TransFirst": + if exp.trans: + script += self.get_indent_string(indent) + "if count < num_trans:\n" + script = self.do_trans_run(script, exp, indent + 1, settings) + if exp.sans: + script += self.get_indent_string(indent) + "if count < num_sans:\n" + script = self.do_sans_run(script, exp, indent + 1, settings) + else: + if exp.sans: + script += self.get_indent_string(indent) + "if count < num_sans:\n" + script = self.do_sans_run(script, exp, indent + 1, settings) + if exp.trans: + script += self.get_indent_string(indent) + "if count < num_trans:\n" + script = self.do_trans_run(script, exp, indent + 1, settings) + script += self.get_indent_string(indent) + "count += 1\n" + script += self.get_indent_string(indent) + "if count >= num_trans and count >= num_sans: break\n" + return script + + def do_trans_run(self, script, exp, indent, settings): + script += self.do_sample_environment(exp, indent) + waitfor = "uamps" + rtype = "histogram" + fulltitle = f"{{{exp.sample}}}" + if settings.collection_mode == "Events": + rtype = "event" + if exp.trans_wait == "Frames": + waitfor = "frames" + elif exp.trans_wait == "Seconds": + waitfor = "seconds" + elif exp.trans_wait == "Minutes": + waitfor = "minutes" + if self.is_larmor or self.is_zoom: + script += self.get_indent_string(indent) + f"do_trans(title='{fulltitle}', pos='{exp.position}', {waitfor}={exp.trans}, thickness={exp.thickness})\n" + elif self.is_loq: + script += self.get_indent_string(indent) + f"do_trans(title='{fulltitle}', pos='{exp.position}', {waitfor}={exp.trans}, thickness={exp.thickness}, aperture='small')\n" + else: + script += self.get_indent_string(indent) + f"do_trans(title='{fulltitle}', pos='{exp.position}', {waitfor}={exp.trans}, thickness={exp.thickness}, dae='transmission', aperture='{self.aperture_string(settings.a2_setting_trans)}')\n" + script += self.get_indent_string(indent) + exp.post_command + "\n" + return script + + def do_sans_run(self, script, exp, indent, settings): + script += self.do_sample_environment(exp, indent) + waitfor = "uamps" + rtype = "histogram" + fulltitle = f"{{{exp.sample}}}" + if exp.background: + fulltitle += f"_{{{exp.background}}}" + if settings.collection_mode in ["Events", "TRANSSANS_Events"]: + rtype = "event" + elif settings.collection_mode == "SESANS": + rtype = "sesans" + if settings.collection_mode in ["TRANSSANS_Events", "TRANSSANS_Histogram"]: + fulltitle += "_TRANS" + if exp.sans_wait == "Frames": + waitfor = "frames" + elif exp.sans_wait == "Seconds": + waitfor = "seconds" + elif exp.sans_wait == "Minutes": + waitfor = "minutes" + if self.is_larmor or self.is_zoom: + script += self.get_indent_string(indent) + f"do_sans(title='{fulltitle}', pos='{exp.position}', {waitfor}={exp.sans}, thickness={exp.thickness}, dae='{rtype}')\n" + else: + script += self.get_indent_string(indent) + f"do_sans(title='{fulltitle}', pos='{exp.position}', {waitfor}={exp.sans}, thickness={exp.thickness}, dae='{rtype}', aperture='{self.aperture_string(settings.a2_setting_sans)}')\n" + script += self.get_indent_string(indent) + exp.post_command + "\n" + return script + + def aperture_string(self, size): + if size in [A2Setting.Large, A2Setting.Larmor]: + return "large" + elif size in [A2Setting.Medium, A2Setting.Larmor]: + return "medium" + elif size in [A2Setting.Small, A2Setting.Larmor]: + return "small" + elif size in [A2Setting.XLarge, A2Setting.Larmor]: + return "xlarge" + return "" + + def do_sample_environment(self, exp, indent): + script = "" + if exp.temperature1: + temp1 = float(exp.temperature1) + temp1low = temp1 - 1.0 + temp1high = temp1 + 1.0 + if self.is_loq: + script += self.get_indent_string(indent) + f"g.cset(internal_setpoint_1={exp.temperature1})\n" + script += self.get_indent_string(indent) + f"g.cset(\"Julabo_1_ext\", lowlimit={temp1low}, highlimit={temp1high}, runcontrol=True)\n" + else: + script += self.get_indent_string(indent) + f"g.cset(JULABO1_SP={exp.temperature1})\n" + script += self.get_indent_string(indent) + f"g.cset(\"JULABO1_TEMP\", lowlimit={temp1low}, highlimit={temp1high}, runcontrol=True)\n" + if exp.temperature2: + temp2 = float(exp.temperature2) + temp2low = temp2 - 1.0 + temp2high = temp2 + 1.0 + if self.is_loq: + script += self.get_indent_string(indent) + f"g.cset(internal_setpoint_2={exp.temperature2})\n" + script += self.get_indent_string(indent) + f"g.cset(\"Julabo_2_ext\", lowlimit={temp2low}, highlimit={temp2high}, runcontrol=True)\n" + else: + script += self.get_indent_string(indent) + f"g.cset(JULABO2_SP={exp.temperature2})\n" + script += self.get_indent_string(indent) + f"g.cset(\"JULABO2_TEMP\", lowlimit={temp2low}, highlimit={temp2high}, runcontrol=True)\n" + if exp.pre_command: + script += self.get_indent_string(indent) + exp.pre_command + "\n" + if exp.field: + script += self.get_indent_string(indent) + f"g.cset(FIELD={exp.field})\n" + if exp.shear_rate1: + script += self.get_indent_string(indent) + f"g.cset(SHEAR_RATE_1={exp.shear_rate1})\n" + if exp.shear_rate2: + script += self.get_indent_string(indent) + f"g.cset(SHEAR_RATE_2={exp.shear_rate2})\n" + if exp.shear_angle1: + script += self.get_indent_string(indent) + f"g.cset(SHEAR_ANGLE_1={exp.shear_angle1})\n" + if exp.shear_angle2: + script += self.get_indent_string(indent) + f"g.cset(SHEAR_ANGLE_2={exp.shear_angle2})\n" + return script + + def estimate_run_time(self, runs, settings, estimation): + minutes = 0 + if settings.num_sans > 0: + minutes += self.calculate_time(runs, True, estimation.count_rate, estimation.move_time) * settings.num_sans + if settings.num_trans > 0: + minutes += self.calculate_time(runs, False, estimation.count_rate, estimation.move_time) * settings.num_trans + time = datetime(1, 1, 1, 0, 0, 0) + timedelta(minutes=minutes) + return time.strftime("%H:%M:%S") + + @staticmethod + def calculate_time(runs, is_sans, count_rate, seconds_between): + minutes = 0 + for exp in runs: + if exp.position: + if is_sans: + minutes += GeniePythonScriptGenerator.get_minutes(exp.sans_wait, exp.sans, count_rate) + else: + minutes += GeniePythonScriptGenerator.get_minutes(exp.trans_wait, exp.trans, count_rate) + minutes += seconds_between / 60 + return minutes + + @staticmethod + def get_minutes(wait, length, count_rate): + if wait == "Minutes": + return length + elif wait == "Seconds": + return length / 60.0 + elif wait == "Frames": + return length / (25.0 * 60.0) + else: + return length / (count_rate / 60.0) + +class A2Setting: + Large = "Large" + Medium = "Medium" + Small = "Small" + XLarge = "XLarge" + Larmor = "Larmor" + + + + + + + +if __name__ == "__main__": + runs = [Experiment(position="pos1", trans=10, sans=20, trans_wait="Minutes", sans_wait="Minutes", sample="Sample1", thickness=1.0)] + settings = ExperimentSettings(sample_width="10", sample_height="10", sample_geometry="Flat", order="AllTrans", num_trans=1, num_sans=1, collection_mode="Events", a2_setting_trans="Large", a2_setting_sans="Large") + estimation = Estimation(count_rate=100, move_time=10) + generator = GeniePythonScriptGenerator(is_larmor=True, is_loq=False, is_zoom=False, is_sans2d=False) + script = generator.generate_script(runs, settings, "my_script") + print(script) + estimated_time = generator.estimate_run_time(runs, settings, estimation) + print(f"Estimated run time: {estimated_time}") \ No newline at end of file diff --git a/Scripting/IScriptGenerator.cs b/Scripting/IScriptGenerator.cs new file mode 100644 index 0000000..fdddd91 --- /dev/null +++ b/Scripting/IScriptGenerator.cs @@ -0,0 +1,11 @@ +using System; +using System.Collections.ObjectModel; + +namespace LOQ_Script_Gui.Scripting +{ + interface IScriptGenerator + { + string GenerateScript(ObservableCollection runs, ExperimentSettings settings, String scriptname); + string EstimateRunTime(ObservableCollection runs, ExperimentSettings settings, Estimation estimation); + } +} diff --git a/Scripting/IScriptGenerator.py b/Scripting/IScriptGenerator.py new file mode 100644 index 0000000..4b8aca1 --- /dev/null +++ b/Scripting/IScriptGenerator.py @@ -0,0 +1,10 @@ +from abc import ABC, abstractmethod + +class IScriptGenerator(ABC): + @abstractmethod + def generate_script(self, runs, settings, scriptname): + pass + + @abstractmethod + def estimate_run_time(self, runs, settings, estimation): + pass diff --git a/Scripting/OpenGenieScriptGenerator.cs b/Scripting/OpenGenieScriptGenerator.cs new file mode 100644 index 0000000..9c9f5b7 --- /dev/null +++ b/Scripting/OpenGenieScriptGenerator.cs @@ -0,0 +1,627 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Collections.ObjectModel; + +namespace LOQ_Script_Gui.Scripting +{ + class OpenGenieScriptGenerator : IScriptGenerator + { + private bool isSans2d = false; + + public OpenGenieScriptGenerator(bool isSans2d) + { + this.isSans2d = isSans2d; + } + + public string GenerateScript(ObservableCollection runs, ExperimentSettings settings, String scriptname) + { + string script = "# Script created by LOQ Script at " + DateTime.Now + Environment.NewLine; + script += "# Script MUST be loaded in to Open GENIE using the loadscript command." + Environment.NewLine + Environment.NewLine; + + if (isSans2d) + { + script += "QUALIFIERS /test" + Environment.NewLine; + } + + script += string.Format("LOCAL transCount = 0 sansCount = 0 numTRANS={0} numSANS={1}", settings.NumTrans, settings.NumSans); + script += Environment.NewLine + Environment.NewLine; + + script += "SETSCRIPTNAME THIS_PROCEDURE()" + Environment.NewLine + Environment.NewLine; + + script += string.Format("Sample_par width={0} height={1} geometry=\"{2}\"", settings.SampleWidth, settings.SampleHeight, settings.SampleGeometry); + script += Environment.NewLine + Environment.NewLine; + + if (isSans2d) + { + string positionlist = "\""; + + foreach (Experiment exp in runs) + { + if (!string.IsNullOrWhiteSpace(exp.Position)) + { + positionlist += exp.Position + ","; + } + } + + if (positionlist.Length > 1) + { + if (positionlist.EndsWith(",")) + { + positionlist = positionlist.Substring(0, positionlist.Length - 1); + } + + positionlist += "\""; + + script += string.Format("IF NOT CHECK_MOVE_POS({0})", positionlist); + script += Environment.NewLine; + script += " PRINTEN \"Invalid positions - script aborted\"" + Environment.NewLine; + script += " RETURN" + Environment.NewLine; + script += "ENDIF" + Environment.NewLine + Environment.NewLine; + + } + + script += "IF test" + Environment.NewLine; + script += " RETURN" + Environment.NewLine; + script += "ENDIF" + Environment.NewLine + Environment.NewLine; + } + + if (settings.Order == RunOrder.AllTrans) + { + // Do all trans first + if (settings.LoopPerRun) + { + script += DoAllTransLoopEach(runs, settings, true, true); + script += DoAllSansLoopEach(runs, settings, true, true); + } + else + { + script += "LOOP" + Environment.NewLine + Environment.NewLine; + script += DoAllTrans(runs, settings, true, true); + script += DoAllSans(runs, settings, true, true); + script += "EXITIF ((transCount >= numTRANS) AND (sansCount >= numSANS))" + Environment.NewLine + Environment.NewLine; + script += "ENDLOOP" + Environment.NewLine + Environment.NewLine; + } + } + else if (settings.Order == RunOrder.TransFirst) + { + // Do Trans-Sans-Trans-Sans... + if (settings.LoopPerRun) + { + foreach (Experiment exp in runs) + { + script += DoSingleTransLoopEach(exp, settings, true, false); + script += DoSingleSansLoopEach(exp, settings, false, true); + } + } + else + { + script += "LOOP" + Environment.NewLine + Environment.NewLine; + + foreach (Experiment exp in runs) + { + script += DoSingleTrans(exp, settings, true, false); + script += DoSingleSans(exp, settings, false, true); + } + + script += "transCount = transCount + 1" + Environment.NewLine; + script += "sansCount = sansCount + 1" + Environment.NewLine + Environment.NewLine; + script += "EXITIF ((transCount >= numTRANS) AND (sansCount >= numSANS))" + Environment.NewLine + Environment.NewLine; + script += "ENDLOOP" + Environment.NewLine + Environment.NewLine; + } + } + else if (settings.Order == RunOrder.AllSans) + { + // Do All Sans first + if (settings.LoopPerRun) + { + script += DoAllSansLoopEach(runs, settings, true, true); + script += DoAllTransLoopEach(runs, settings, true, true); + } + else + { + script += "LOOP" + Environment.NewLine + Environment.NewLine; + script += DoAllSans(runs, settings, true, true); + script += DoAllTrans(runs, settings, true, true); + script += "EXITIF ((transCount >= numTRANS) AND (sansCount >= numSANS))" + Environment.NewLine + Environment.NewLine; + script += "ENDLOOP" + Environment.NewLine + Environment.NewLine; + } + } + else if (settings.Order == RunOrder.SansFirst) + { + // Do Sans-Trans-Sans-Trans... + if (settings.LoopPerRun) + { + foreach (Experiment exp in runs) + { + script += DoSingleSansLoopEach(exp, settings, true, false); + script += DoSingleTransLoopEach(exp, settings, false, true); + } + } + else + { + script += "LOOP" + Environment.NewLine + Environment.NewLine; + + foreach (Experiment exp in runs) + { + script += DoSingleSans(exp, settings, true, false); + script += DoSingleTrans(exp, settings, false, true); + } + + script += "transCount = transCount + 1" + Environment.NewLine; + script += "sansCount = sansCount + 1" + Environment.NewLine + Environment.NewLine; + script += "EXITIF ((transCount >= numTRANS) AND (sansCount >= numSANS))" + Environment.NewLine + Environment.NewLine; + script += "ENDLOOP" + Environment.NewLine + Environment.NewLine; + } + } + + //script += "ENDPROCEDURE" + Environment.NewLine; + script += "PRINTN \"SCRIPT COMPLETED \"" + Environment.NewLine + Environment.NewLine; + + return script; + } + + private static string DoSingleSans(Experiment exp, ExperimentSettings settings, bool doPreCom, bool doPostCom) + { + string script = ""; + + if (settings.NumSans > 0) + { + if (exp.Sans > 0) + { + script += "IF (sansCount < numSANS)" + Environment.NewLine; + script += setSansAperture(settings.A2SettingSans); + script += DoSingleRun(false, exp, false, doPreCom, doPostCom, settings); + script += Environment.NewLine + "ENDIF" + Environment.NewLine + Environment.NewLine; + } + } + return script; + } + + private static string DoSingleTrans(Experiment exp, ExperimentSettings settings, bool doPreCom, bool doPostCom) + { + string script = ""; + + if (settings.NumTrans > 0) + { + if (exp.Trans > 0) + { + script += "IF (transCount < numTRANS)" + Environment.NewLine; + script += setTransAperture(settings.A2SettingTrans); + script += DoSingleRun(true, exp, false, doPreCom, doPostCom, settings); + script += Environment.NewLine + "ENDIF" + Environment.NewLine + Environment.NewLine; + } + } + return script; + } + + private static string DoSingleSansLoopEach(Experiment exp, ExperimentSettings settings, bool doPreCom, bool doPostCom) + { + string script = ""; + + if (settings.NumSans > 0) + { + if (exp.Sans > 0) + { + script += setSansAperture(settings.A2SettingSans); + script += DoSingleRun(false, exp, true, doPreCom, doPostCom, settings) + Environment.NewLine; + } + } + return script; + } + + private static string DoSingleTransLoopEach(Experiment exp, ExperimentSettings settings, bool doPreCom, bool doPostCom) + { + string script = ""; + + if (settings.NumTrans > 0) + { + if (exp.Trans > 0) + { + script += setTransAperture(settings.A2SettingTrans); + script += DoSingleRun(true, exp, true, doPreCom, doPostCom,settings) + Environment.NewLine; + } + } + return script; + } + + private static string DoAllSans(ObservableCollection runs, ExperimentSettings settings, bool doPreCom, bool doPostCom) + { + string script = ""; + + if (settings.NumSans > 0) + { + script += setSansAperture(settings.A2SettingSans); + + script += "IF sansCount < numSANS" + Environment.NewLine + Environment.NewLine; + + // Loop through each experiment + foreach (Experiment exp in runs) + { + script += DoSingleRun(false, exp, false, doPreCom, doPostCom, settings) + Environment.NewLine; + } + + script += "sansCount = sansCount + 1" + Environment.NewLine + Environment.NewLine; + script += "ENDIF" + Environment.NewLine + Environment.NewLine; + } + + return script; + } + + private static string DoAllTrans(ObservableCollection runs, ExperimentSettings settings, bool doPreCom, bool doPostCom) + { + string script = ""; + + if (settings.NumTrans > 0) + { + script += setTransAperture(settings.A2SettingTrans); + + script += "IF transCount < numTRANS" + Environment.NewLine + Environment.NewLine; + + // Loop through each experiment + foreach (Experiment exp in runs) + { + script += DoSingleRun(true, exp, false, doPreCom, doPostCom, settings) + Environment.NewLine; + } + + script += "transCount = transCount + 1" + Environment.NewLine + Environment.NewLine; + script += "ENDIF" + Environment.NewLine + Environment.NewLine; + } + + return script; + } + + private static string DoAllSansLoopEach(ObservableCollection runs, ExperimentSettings settings, bool doPreCom, bool doPostCom) + { + string script = ""; + + if (settings.NumSans > 0) + { + script += setSansAperture(settings.A2SettingSans); + + // Loop through each experiment + foreach (Experiment exp in runs) + { + script += DoSingleRun(false, exp, true, doPreCom, doPostCom, settings) + Environment.NewLine; + } + } + + return script; + } + + private static string DoAllTransLoopEach(ObservableCollection runs, ExperimentSettings settings, bool doPreCom, bool doPostCom) + { + string script = ""; + + if (settings.NumTrans > 0) + { + script += setTransAperture(settings.A2SettingTrans); + + // Loop through each experiment + foreach (Experiment exp in runs) + { + script += DoSingleRun(true, exp, true, doPreCom, doPostCom, settings) + Environment.NewLine; + } + } + + return script; + } + + private static string setSansAperture(string A2SizeSans) + { + string script = ""; + + if (A2SizeSans == A2Setting.Large) + { + script += "DO_SANS /LARGE" + Environment.NewLine + Environment.NewLine; + } + else if (A2SizeSans == A2Setting.Medium) + { + script += "DO_SANS /MEDIUM" + Environment.NewLine + Environment.NewLine; + } + else if (A2SizeSans == A2Setting.Small) + { + script += "DO_SANS /SMALL" + Environment.NewLine + Environment.NewLine; + } + else if (A2SizeSans == A2Setting.Trans) + { + script += "DO_SANS /TRANS" + Environment.NewLine + Environment.NewLine; + } + + return script; + } + + private static string setTransAperture(string A2SizeTrans) + { + string script = ""; + + if (string.IsNullOrWhiteSpace(A2SizeTrans)) + { + script += "DO_TRANS" + Environment.NewLine + Environment.NewLine; + } + else + { + if (A2SizeTrans == A2Setting.Large) + { + script += "DO_TRANS /LARGE" + Environment.NewLine + Environment.NewLine; + } + else if (A2SizeTrans == A2Setting.Medium) + { + script += "DO_TRANS /MEDIUM" + Environment.NewLine + Environment.NewLine; + } + else if (A2SizeTrans == A2Setting.Small) + { + script += "DO_TRANS /SMALL" + Environment.NewLine + Environment.NewLine; + } + else if (A2SizeTrans == A2Setting.Trans) + { + script += "DO_TRANS /TRANS" + Environment.NewLine + Environment.NewLine; + } + } + + return script; + } + + private static string DoSingleRun(bool isTrans, Experiment exp, bool isLoop, bool doPreCom, bool doPostCom, ExperimentSettings settings) + { + string script = ""; + + if (exp.Trans > 0 || exp.Sans > 0) + { + if (!string.IsNullOrWhiteSpace(exp.RbNumber)) + { + script += "change rbno=" + exp.RbNumber + Environment.NewLine; + } + + if (!string.IsNullOrWhiteSpace(exp.Position)) + { + if (doPreCom) + { + script += DoSampleEnvironment(exp); + } + + if (isLoop) + { + if (isTrans) + { + script += "LOOP transCount FROM 1 TO numTRANS" + Environment.NewLine; + } + else + { + script += "LOOP sansCount FROM 1 TO numSANS" + Environment.NewLine; + } + + script += CreateMoveCommand(isTrans, exp, settings); + script += "ENDLOOP" + Environment.NewLine; + } + else + { + script += CreateMoveCommand(isTrans, exp, settings); + } + + // Check for post-command - always run last + if (doPostCom && !string.IsNullOrWhiteSpace(exp.PostCommand)) + { + script += exp.PostCommand + Environment.NewLine; + } + } + } + + return script; + } + + private static string DoSampleEnvironment(Experiment exp) + { + string script = ""; + + // Set Temps first + if (!string.IsNullOrWhiteSpace(exp.Temperature1)) + { + script += "CSET TEMP=" + exp.Temperature1 + Environment.NewLine; + } + + if (!string.IsNullOrWhiteSpace(exp.Temperature2)) + { + script += "CSET TEMP2=" + exp.Temperature2 + Environment.NewLine; + } + + // Check for pre-command - always run immediately after the temp is set + if (!string.IsNullOrWhiteSpace(exp.PreCommand)) + { + script += exp.PreCommand + Environment.NewLine; + } + + if (!string.IsNullOrWhiteSpace(exp.Field)) + { + script += "CSET FIELD=" + exp.Field + Environment.NewLine; + } + + if (!string.IsNullOrWhiteSpace(exp.ShearRate1)) + { + script += "CSET SHEAR_RATE_1=" + exp.ShearRate1 + Environment.NewLine; + } + + if (!string.IsNullOrWhiteSpace(exp.ShearRate2)) + { + script += "CSET SHEAR_RATE_2=" + exp.ShearRate2 + Environment.NewLine; + } + + if (!string.IsNullOrWhiteSpace(exp.ShearAngle1)) + { + script += "CSET SHEAR_ANGLE_1=" + exp.ShearAngle1 + Environment.NewLine; + } + + if (!string.IsNullOrWhiteSpace(exp.ShearAngle2)) + { + script += "CSET SHEAR_ANGLE_2=" + exp.ShearAngle2 + Environment.NewLine; + } + + return script; + } + + private static string CreateMoveCommand(bool isTrans, Experiment exp, ExperimentSettings settings) + { + string script = "MOVE pos=\""+exp.Position+"\""; + string fulltitle = ""; + + + if (!string.IsNullOrWhiteSpace(exp.Sample)) + { + fulltitle = "{" + exp.Sample + "}"; + } + + if (!string.IsNullOrWhiteSpace(exp.Background)) + { + fulltitle += "_{"+exp.Background+"}"; + } + + if (!string.IsNullOrWhiteSpace(exp.Thickness)) + { + script += " thick=" + exp.Thickness; + } + else + { + script += " thick=1"; + } + + if (!string.IsNullOrWhiteSpace(exp.Period)) + { + script += " period=" + exp.Period; + } + + if (isTrans) + { + script += WriteWaitFor(exp.TransWait, exp.Trans); + + if (!string.IsNullOrWhiteSpace(fulltitle)) + { + script += string.Format(" title=\"{0}_TRANS\"", "{"+exp.Sample+"}"); + } + } + else + { + script += WriteWaitFor(exp.SansWait, exp.Sans); + + if (!string.IsNullOrWhiteSpace(fulltitle)) + { + if (settings.CollectionMode == DataCollection.TRANSSANS_Events || settings.CollectionMode == DataCollection.TRANSSANS_Histogram) + { + script += string.Format(" title=\"{0}_TRANS_SANS\"", fulltitle); + } + else + { + script += string.Format(" title=\"{0}_SANS\"", fulltitle); + } + } + } + + return script + Environment.NewLine; + + } + + private static string WriteWaitFor(string waitfor, double length) + { + string script = ""; + + if (!string.IsNullOrWhiteSpace(waitfor)) + { + if (waitfor.ToLower() == WaitForUnits.MicroAmps.ToLower()) + { + script += " uAhr=" + length; + } + else if (waitfor.ToLower() == WaitForUnits.Minutes.ToLower()) + { + script += " min=" + length; + } + else if (waitfor.ToLower() == WaitForUnits.Seconds.ToLower()) + { + script += " seconds=" + length; + } + else if (waitfor.ToLower() == WaitForUnits.Frames.ToLower()) + { + script += " frame=" + length; + } + else + { + // If in doubt use micro-amps + script += " uAhr=" + length; + } + } + else + { + // If in doubt use micro-amps + script += " uAhr=" + length; + } + return script; + } + + public string EstimateRunTime(ObservableCollection runs, ExperimentSettings settings, Estimation estimation) + { + double minutes = 0; + + if (settings.NumSans > 0) + { + minutes += CalculateTime(runs, true, estimation.CountRate, estimation.MoveTime) * settings.NumSans; + } + + if (settings.NumTrans > 0) + { + minutes += CalculateTime(runs, false, estimation.CountRate, estimation.MoveTime) * settings.NumTrans; + } + + DateTime time = new DateTime(1, 1, 1, 0, 0, 0); + + time = time.AddMinutes(minutes); + + return time.ToLongTimeString(); + } + + private static double CalculateTime(ObservableCollection runs, bool isSans, double countRate, double secondsBetween) + { + double minutes = 0; + + foreach (Experiment exp in runs) + { + if (!string.IsNullOrWhiteSpace(exp.Position)) + { + if (isSans) + { + minutes += GetMinutes(exp.SansWait, exp.Sans, countRate); + } + else + { + minutes += GetMinutes(exp.TransWait, exp.Trans, countRate); + } + + minutes += secondsBetween / 60; + } + } + + return minutes; + } + + private static double GetMinutes(string wait, double length, double countRate) + { + if (wait == WaitForUnits.Minutes) + { + return length; + } + else if (wait == WaitForUnits.Seconds) + { + return length / 60.0; + } + else if (wait == WaitForUnits.Frames) + { + return length / (25.0 * 60.0); + } + else + { + // If in doubt use micro-amps + return length / (countRate / 60.0); + } + } + + } +} diff --git a/Scripting/OpenGenieScriptGenerator.py b/Scripting/OpenGenieScriptGenerator.py new file mode 100644 index 0000000..f8329ef --- /dev/null +++ b/Scripting/OpenGenieScriptGenerator.py @@ -0,0 +1,309 @@ +from datetime import datetime, timedelta +from Models.Estimation import Estimation +from Models.Experiment import Experiment +from Models.ExperimentSettings import ExperimentSettings + +class OpenGenieScriptGenerator: + def __init__(self, is_sans2d): + self.is_sans2d = is_sans2d + + def generate_script(self, runs, settings, scriptname): + script = f"# Script created by LOQ Script at {datetime.now()}\n" + script += "# Script MUST be loaded in to Open GENIE using the loadscript command.\n\n" + + if self.is_sans2d: + script += "QUALIFIERS /test\n" + + script += f"LOCAL transCount = 0 sansCount = 0 numTRANS={settings.num_trans} numSANS={settings.num_sans}\n\n" + script += "SETSCRIPTNAME THIS_PROCEDURE()\n\n" + script += f'Sample_par width={settings.sample_width} height={settings.sample_height} geometry="{settings.sample_geometry}"\n\n' + + if self.is_sans2d: + position_list = ",".join([exp.position for exp in runs if exp.position]) + if position_list: + script += f'IF NOT CHECK_MOVE_POS("{position_list}")\n' + script += ' PRINTEN "Invalid positions - script aborted"\n' + script += ' RETURN\n' + script += 'ENDIF\n\n' + script += "IF test\n" + script += " RETURN\n" + script += "ENDIF\n\n" + + if settings.order == "AllTrans": + if settings.loop_per_run: + script += self.do_all_trans_loop_each(runs, settings, True, True) + script += self.do_all_sans_loop_each(runs, settings, True, True) + else: + script += "LOOP\n\n" + script += self.do_all_trans(runs, settings, True, True) + script += self.do_all_sans(runs, settings, True, True) + script += "EXITIF ((transCount >= numTRANS) AND (sansCount >= numSANS))\n\n" + script += "ENDLOOP\n\n" + elif settings.order == "TransFirst": + if settings.loop_per_run: + for exp in runs: + script += self.do_single_trans_loop_each(exp, settings, True, False) + script += self.do_single_sans_loop_each(exp, settings, False, True) + else: + script += "LOOP\n\n" + for exp in runs: + script += self.do_single_trans(exp, settings, True, False) + script += self.do_single_sans(exp, settings, False, True) + script += "transCount = transCount + 1\n" + script += "sansCount = sansCount + 1\n\n" + script += "EXITIF ((transCount >= numTRANS) AND (sansCount >= numSANS))\n\n" + script += "ENDLOOP\n\n" + elif settings.order == "AllSans": + if settings.loop_per_run: + script += self.do_all_sans_loop_each(runs, settings, True, True) + script += self.do_all_trans_loop_each(runs, settings, True, True) + else: + script += "LOOP\n\n" + script += self.do_all_sans(runs, settings, True, True) + script += self.do_all_trans(runs, settings, True, True) + script += "EXITIF ((transCount >= numTRANS) AND (sansCount >= numSANS))\n\n" + script += "ENDLOOP\n\n" + elif settings.order == "SansFirst": + if settings.loop_per_run: + for exp in runs: + script += self.do_single_sans_loop_each(exp, settings, True, False) + script += self.do_single_trans_loop_each(exp, settings, False, True) + else: + script += "LOOP\n\n" + for exp in runs: + script += self.do_single_sans(exp, settings, True, False) + script += self.do_single_trans(exp, settings, False, True) + script += "transCount = transCount + 1\n" + script += "sansCount = sansCount + 1\n\n" + script += "EXITIF ((transCount >= numTRANS) AND (sansCount >= numSANS))\n\n" + script += "ENDLOOP\n\n" + + script += 'PRINTN "SCRIPT COMPLETED "\n\n' + return script + + def do_single_sans(self, exp, settings, do_pre_com, do_post_com): + script = "" + if settings.num_sans > 0 and exp.sans > 0: + script += "IF (sansCount < numSANS)\n" + script += self.set_sans_aperture(settings.a2_setting_sans) + script += self.do_single_run(False, exp, False, do_pre_com, do_post_com, settings) + script += "\nENDIF\n\n" + return script + + def do_single_trans(self, exp, settings, do_pre_com, do_post_com): + script = "" + if settings.num_trans > 0 and exp.trans > 0: + script += "IF (transCount < numTRANS)\n" + script += self.set_trans_aperture(settings.a2_setting_trans) + script += self.do_single_run(True, exp, False, do_pre_com, do_post_com, settings) + script += "\nENDIF\n\n" + return script + + def do_single_sans_loop_each(self, exp, settings, do_pre_com, do_post_com): + script = "" + if settings.num_sans > 0 and exp.sans > 0: + script += self.set_sans_aperture(settings.a2_setting_sans) + script += self.do_single_run(False, exp, True, do_pre_com, do_post_com, settings) + "\n" + return script + + def do_single_trans_loop_each(self, exp, settings, do_pre_com, do_post_com): + script = "" + if settings.num_trans > 0 and exp.trans > 0: + script += self.set_trans_aperture(settings.a2_setting_trans) + script += self.do_single_run(True, exp, True, do_pre_com, do_post_com, settings) + "\n" + return script + + def do_all_sans(self, runs, settings, do_pre_com, do_post_com): + script = "" + if settings.num_sans > 0: + script += self.set_sans_aperture(settings.a2_setting_sans) + script += "IF sansCount < numSANS\n\n" + for exp in runs: + script += self.do_single_run(False, exp, False, do_pre_com, do_post_com, settings) + "\n" + script += "sansCount = sansCount + 1\n\n" + script += "ENDIF\n\n" + return script + + def do_all_trans(self, runs, settings, do_pre_com, do_post_com): + script = "" + if settings.num_trans > 0: + script += self.set_trans_aperture(settings.a2_setting_trans) + script += "IF transCount < numTRANS\n\n" + for exp in runs: + script += self.do_single_run(True, exp, False, do_pre_com, do_post_com, settings) + "\n" + script += "transCount = transCount + 1\n\n" + script += "ENDIF\n\n" + return script + + def do_all_sans_loop_each(self, runs, settings, do_pre_com, do_post_com): + script = "" + if settings.num_sans > 0: + script += self.set_sans_aperture(settings.a2_setting_sans) + for exp in runs: + script += self.do_single_run(False, exp, True, do_pre_com, do_post_com, settings) + "\n" + return script + + def do_all_trans_loop_each(self, runs, settings, do_pre_com, do_post_com): + script = "" + if settings.num_trans > 0: + script += self.set_trans_aperture(settings.a2_setting_trans) + for exp in runs: + script += self.do_single_run(True, exp, True, do_pre_com, do_post_com, settings) + "\n" + return script + + def set_sans_aperture(self, a2_size_sans): + script = "" + if a2_size_sans == "Large": + script += "DO_SANS /LARGE\n\n" + elif a2_size_sans == "Medium": + script += "DO_SANS /MEDIUM\n\n" + elif a2_size_sans == "Small": + script += "DO_SANS /SMALL\n\n" + elif a2_size_sans == "Trans": + script += "DO_SANS /TRANS\n\n" + return script + + def set_trans_aperture(self, a2_size_trans): + script = "" + if not a2_size_trans: + script += "DO_TRANS\n\n" + else: + if a2_size_trans == "Large": + script += "DO_TRANS /LARGE\n\n" + elif a2_size_trans == "Medium": + script += "DO_TRANS /MEDIUM\n\n" + elif a2_size_trans == "Small": + script += "DO_TRANS /SMALL\n\n" + elif a2_size_trans == "Trans": + script += "DO_TRANS /TRANS\n\n" + return script + + def do_single_run(self, is_trans, exp, is_loop, do_pre_com, do_post_com, settings): + script = "" + if exp.trans > 0 or exp.sans > 0: + if exp.rb_number: + script += f"change rbno={exp.rb_number}\n" + if exp.position: + if do_pre_com: + script += self.do_sample_environment(exp) + if is_loop: + if is_trans: + script += "LOOP transCount FROM 1 TO numTRANS\n" + else: + script += "LOOP sansCount FROM 1 TO numSANS\n" + script += self.create_move_command(is_trans, exp, settings) + script += "ENDLOOP\n" + else: + script += self.create_move_command(is_trans, exp, settings) + if do_post_com and exp.post_command: + script += exp.post_command + "\n" + return script + + def do_sample_environment(self, exp): + script = "" + if exp.temperature1: + script += f"CSET TEMP={exp.temperature1}\n" + if exp.temperature2: + script += f"CSET TEMP2={exp.temperature2}\n" + if exp.pre_command: + script += exp.pre_command + "\n" + if exp.field: + script += f"CSET FIELD={exp.field}\n" + if exp.shear_rate1: + script += f"CSET SHEAR_RATE_1={exp.shear_rate1}\n" + if exp.shear_rate2: + script += f"CSET SHEAR_RATE_2={exp.shear_rate2}\n" + if exp.shear_angle1: + script += f"CSET SHEAR_ANGLE_1={exp.shear_angle1}\n" + if exp.shear_angle2: + script += f"CSET SHEAR_ANGLE_2={exp.shear_angle2}\n" + return script + + def create_move_command(self, is_trans, exp, settings): + script = f'MOVE pos="{exp.position}"' + fulltitle = f"{{{exp.sample}}}" + if exp.background: + fulltitle += f"_{{{exp.background}}}" + if exp.thickness: + script += f" thick={exp.thickness}" + else: + script += " thick=1" + if exp.period: + script += f" period={exp.period}" + if is_trans: + script += self.write_wait_for(exp.trans_wait, exp.trans) + if fulltitle: + script += f' title="{fulltitle}_TRANS"' + else: + script += self.write_wait_for(exp.sans_wait, exp.sans) + if fulltitle: + if settings.collection_mode in ["TRANSSANS_Events", "TRANSSANS_Histogram"]: + script += f' title="{fulltitle}_TRANS_SANS"' + else: + script += f' title="{fulltitle}_SANS"' + return script + "\n" + + def write_wait_for(self, waitfor, length): + script = "" + if waitfor: + if waitfor.lower() == "MicroAmps".lower(): + script += f" uAhr={length}" + elif waitfor.lower() == "Minutes".lower(): + script += f" min={length}" + elif waitfor.lower() == "Seconds".lower(): + script += f" seconds={length}" + elif waitfor.lower() == "Frames".lower(): + script += f" frame={length}" + else: + script += f" uAhr={length}" + else: + script += f" uAhr={length}" + return script + + def estimate_run_time(self, runs, settings, estimation): + minutes = 0 + if settings.num_sans > 0: + minutes += self.calculate_time(runs, True, estimation.count_rate, estimation.move_time) * settings.num_sans + if settings.num_trans > 0: + minutes += self.calculate_time(runs, False, estimation.count_rate, estimation.move_time) * settings.num_trans + time = datetime(1, 1, 1, 0, 0, 0) + timedelta(minutes=minutes) + return time.strftime("%H:%M:%S") + + @staticmethod + def calculate_time(runs, is_sans, count_rate, seconds_between): + minutes = 0 + for exp in runs: + if exp.position: + if is_sans: + minutes += OpenGenieScriptGenerator.get_minutes(exp.sans_wait, exp.sans, count_rate) + else: + minutes += OpenGenieScriptGenerator.get_minutes(exp.trans_wait, exp.trans, count_rate) + minutes += seconds_between / 60 + return minutes + + @staticmethod + def get_minutes(wait, length, count_rate): + if wait == "Minutes": + return length + elif wait == "Seconds": + return length / 60.0 + elif wait == "Frames": + return length / (25.0 * 60.0) + else: + return length / (count_rate / 60.0) + + + + + + + +if __name__ == "__main__": + runs = [Experiment(position="pos1", trans=10, sans=20, trans_wait="Minutes", sans_wait="Minutes", sample="Sample1", thickness=1.0)] + settings = ExperimentSettings(sample_width="10", sample_height="10", sample_geometry="Flat", order="AllTrans", num_trans=1, num_sans=1, collection_mode="Events", a2_setting_trans="Large", a2_setting_sans="Large", loop_per_run=False) + estimation = Estimation(count_rate=100, move_time=10) + generator = OpenGenieScriptGenerator(is_sans2d=True) + script = generator.generate_script(runs, settings, "my_script") + print(script) + estimated_time = generator.estimate_run_time(runs, settings, estimation) + print(f"Estimated run time: {estimated_time}") \ No newline at end of file diff --git a/SettingsPanel.py b/SettingsPanel.py new file mode 100644 index 0000000..2938fed --- /dev/null +++ b/SettingsPanel.py @@ -0,0 +1,98 @@ +import tkinter as tk +from tkinter import ttk +from ViewModels.SettingsPanelVM import SettingsPanelVM + + +class SettingsPanel(ttk.Frame): + def __init__(self, parent, settings_vm=None): + super().__init__(parent) + + self.settings_vm = settings_vm if settings_vm is not None else SettingsPanelVM() + self.on_preview_clicked = None + self.on_write_clicked = None + + self.create_widgets() + + def create_widgets(self): + #self.preview_button = ttk.Button(self, text="Preview Script", command=self.on_preview_clicked) + #self.preview_button.pack(side=tk.LEFT, padx=5, pady=5) + #self.write_button = ttk.Button(self, text="Write Script", command=self.on_write_clicked) + #self.write_button.pack(side=tk.LEFT, padx=5, pady=5) + + + label = ttk.Label(self, text="Settings", font=("Helvetica", 16, "bold")) + label.pack(pady=10) + + grid_frame = ttk.Frame(self) + grid_frame.pack(padx=10, pady=10) + + # Order + ttk.Label(grid_frame, text="Order:").grid(row=0, column=0, sticky=tk.E) + self.com_order = ttk.Combobox(grid_frame, values=self.settings_vm.order_choices) + self.com_order.grid(row=0, column=1, columnspan=2, padx=5, pady=5) + + # Do SANS + ttk.Label(grid_frame, text="Do SANS:").grid(row=1, column=0, sticky=tk.E) + sans_frame = ttk.Frame(grid_frame) + sans_frame.grid(row=1, column=1, columnspan=2) + self.txt_times_sans = ttk.Entry(sans_frame, width=5) + self.txt_times_sans.pack(side=tk.LEFT, padx=5, pady=5) + ttk.Label(sans_frame, text="time(s)").pack(side=tk.LEFT) + + # Do TRANS + ttk.Label(grid_frame, text="Do TRANS:").grid(row=2, column=0, sticky=tk.E) + trans_frame = ttk.Frame(grid_frame) + trans_frame.grid(row=2, column=1, columnspan=2) + self.txt_times_trans = ttk.Entry(trans_frame, width=5) + self.txt_times_trans.pack(side=tk.LEFT, padx=5, pady=5) + ttk.Label(trans_frame, text="time(s)").pack(side=tk.LEFT) + + # Loop over each run + self.chk_loop_run = ttk.Checkbutton(grid_frame, text="Loop over each run?") + self.chk_loop_run.grid(row=3, column=0, columnspan=3, pady=5) + + # Sample Geometry + ttk.Label(grid_frame, text="Sample Geometry:").grid(row=6, column=0, sticky=tk.E) + self.com_sample_geo = ttk.Combobox(grid_frame, values=self.settings_vm.geometry_choices) + self.com_sample_geo.grid(row=6, column=1, columnspan=2, padx=5, pady=5) + + # Sample Height + ttk.Label(grid_frame, text="Sample Height:").grid(row=7, column=0, sticky=tk.E) + sample_height_frame = ttk.Frame(grid_frame) + sample_height_frame.grid(row=7, column=1, columnspan=2) + self.txt_sample_height = ttk.Entry(sample_height_frame, width=5) + self.txt_sample_height.pack(side=tk.LEFT, padx=5, pady=5) + ttk.Label(sample_height_frame, text="mm").pack(side=tk.LEFT) + + # Sample Width + ttk.Label(grid_frame, text="Sample Width:").grid(row=8, column=0, sticky=tk.E) + sample_width_frame = ttk.Frame(grid_frame) + sample_width_frame.grid(row=8, column=1, columnspan=2) + self.txt_sample_width = ttk.Entry(sample_width_frame, width=5) + self.txt_sample_width.pack(side=tk.LEFT, padx=5, pady=5) + ttk.Label(sample_width_frame, text="mm").pack(side=tk.LEFT) + + # Buttons + button_frame = ttk.Frame(self) + button_frame.pack(pady=10) + self.btn_preview_script = ttk.Button(button_frame, text="Preview Script", command=self.btn_preview_script_click) + self.btn_preview_script.pack(side=tk.LEFT, padx=5) + self.btn_write_script = ttk.Button(button_frame, text="Write Script", command=self.btn_write_script_click) + self.btn_write_script.pack(side=tk.LEFT, padx=5) + + def btn_preview_script_click(self): + if self.on_preview_clicked: + self.on_preview_clicked() + + + + def btn_write_script_click(self): + if self.on_write_clicked: + self.on_write_clicked() + + +if __name__ == "__main__": + root = tk.Tk() + panel = SettingsPanel(root) + panel.pack() + root.mainloop() \ No newline at end of file diff --git a/SettingsPanel.xaml b/SettingsPanel.xaml new file mode 100644 index 0000000..8c63f8f --- /dev/null +++ b/SettingsPanel.xaml @@ -0,0 +1,72 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Loop over each run? + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/SettingsPanel.xaml.cs b/SettingsPanel.xaml.cs new file mode 100644 index 0000000..3e768e6 --- /dev/null +++ b/SettingsPanel.xaml.cs @@ -0,0 +1,47 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Imaging; +using System.Windows.Navigation; +using System.Windows.Shapes; + +namespace LOQ_Script_Gui +{ + /// + /// Interaction logic for SettingsPanel.xaml + /// + public partial class SettingsPanel : UserControl + { + public event EventHandler OnPreviewClicked; + public event EventHandler OnWriteClicked; + + public SettingsPanel() + { + InitializeComponent(); + } + + private void btnPreviewScript_Click(object sender, RoutedEventArgs e) + { + if (OnPreviewClicked != null) + { + OnPreviewClicked(sender, e); + } + } + + private void btnWriteScript_Click(object sender, RoutedEventArgs e) + { + if (OnWriteClicked != null) + { + OnWriteClicked(sender, e); + } + } + } +} diff --git a/Transcript_GitCopilot.txt b/Transcript_GitCopilot.txt new file mode 100644 index 0000000..17862f6 --- /dev/null +++ b/Transcript_GitCopilot.txt @@ -0,0 +1,3937 @@ +dehoni: @workspace /explain + +Workspace: The provided code is a C# file named + +SettingsPanel.xaml.cs + +, which is part of a WPF (Windows Presentation Foundation) application. This file defines the code-behind for a user control named + +SettingsPanel + +. The + +SettingsPanel + + class inherits from + +UserControl + +, which is a base class for defining custom user interface controls in WPF. + +The + +SettingsPanel + + class contains two public events: + +OnPreviewClicked + + and + +OnWriteClicked + +. These events are used to notify subscribers when the corresponding buttons are clicked. Events in C# are a way to provide notifications to other parts of the application when something of interest happens. + +The constructor + +SettingsPanel() + + initializes the component by calling the + +InitializeComponent() + + method. This method is automatically generated and is responsible for loading the XAML-defined UI elements and setting up the control. + +There are two private methods in the class: + +btnPreviewScript_Click + + and + +btnWriteScript_Click + +. These methods are event handlers for button click events. When the + +btnPreviewScript_Click + + method is called (which happens when the associated button is clicked), it checks if there are any subscribers to the + +OnPreviewClicked + + event. If there are, it raises the event by invoking the + +OnPreviewClicked + + delegate, passing the sender and event arguments. + +Similarly, the + +btnWriteScript_Click + + method handles the click event for another button. It checks for subscribers to the + +OnWriteClicked + + event and raises the event if there are any. + +Overall, this code sets up a user control with two buttons, each of which can trigger events that other parts of the application can subscribe to and handle accordingly. This allows for a decoupled design where the logic for handling button clicks can be separated from the user control itself. + +dehoni: Please translate the selected code into python and use approbriate packages. + +GitHub Copilot: Here is the translated code into Python using the `tkinter` package, which is a standard GUI toolkit in Python: + +```python + + +import tkinter as tk +from tkinter import ttk + +class SettingsPanel(ttk.Frame): + def __init__(self, parent): + super().__init__(parent) + self.on_preview_clicked = None + self.on_write_clicked = None + + self.preview_button = ttk.Button(self, text="Preview Script", command=self.btn_preview_script_click) + self.preview_button.pack() + + self.write_button = ttk.Button(self, text="Write Script", command=self.btn_write_script_click) + self.write_button.pack() + + def btn_preview_script_click(self): + if self.on_preview_clicked is not None: + self.on_preview_clicked() + + def btn_write_script_click(self): + if self.on_write_clicked is not None: + self.on_write_clicked() + +if __name__ == "__main__": + root = tk.Tk() + panel = SettingsPanel(root) + panel.pack() + root.mainloop() +``` + +In this Python version: +- The + +SettingsPanel + + class inherits from `ttk.Frame`. +- The `on_preview_clicked` and `on_write_clicked` attributes are used to store callback functions. +- The `btn_preview_script_click` and `btn_write_script_click` methods are defined to call these callbacks if they are set. +- The `ttk.Button` widgets are created for the preview and write actions, and their `command` attributes are set to the corresponding methods. +- The `if __name__ == "__main__":` block creates a simple application to demonstrate the + +SettingsPanel + + usage. + +dehoni: Please translate the selected code into python and use approbriate packages. + +GitHub Copilot: Here is the translated code into Python using the `tkinter` package: + +```python + + +import tkinter as tk +from tkinter import ttk + +class SettingsPanel(ttk.Frame): + def __init__(self, parent): + super().__init__(parent) + + self.settings_vm = SettingsPanelVM() + + self.create_widgets() + + def create_widgets(self): + label = ttk.Label(self, text="Settings", font=("Helvetica", 16, "bold")) + label.pack(pady=10) + + grid_frame = ttk.Frame(self) + grid_frame.pack(padx=10, pady=10) + + # Order + ttk.Label(grid_frame, text="Order:").grid(row=0, column=0, sticky=tk.E) + self.com_order = ttk.Combobox(grid_frame, values=self.settings_vm.order_choices) + self.com_order.grid(row=0, column=1, columnspan=2, padx=5, pady=5) + + # Do SANS + ttk.Label(grid_frame, text="Do SANS:").grid(row=1, column=0, sticky=tk.E) + sans_frame = ttk.Frame(grid_frame) + sans_frame.grid(row=1, column=1, columnspan=2) + self.txt_times_sans = ttk.Entry(sans_frame, width=5) + self.txt_times_sans.pack(side=tk.LEFT, padx=5, pady=5) + ttk.Label(sans_frame, text="time(s)").pack(side=tk.LEFT) + + # Do TRANS + ttk.Label(grid_frame, text="Do TRANS:").grid(row=2, column=0, sticky=tk.E) + trans_frame = ttk.Frame(grid_frame) + trans_frame.grid(row=2, column=1, columnspan=2) + self.txt_times_trans = ttk.Entry(trans_frame, width=5) + self.txt_times_trans.pack(side=tk.LEFT, padx=5, pady=5) + ttk.Label(trans_frame, text="time(s)").pack(side=tk.LEFT) + + # Loop over each run + self.chk_loop_run = ttk.Checkbutton(grid_frame, text="Loop over each run?") + self.chk_loop_run.grid(row=3, column=0, columnspan=3, pady=5) + + # Sample Geometry + ttk.Label(grid_frame, text="Sample Geometry:").grid(row=6, column=0, sticky=tk.E) + self.com_sample_geo = ttk.Combobox(grid_frame, values=self.settings_vm.geometry_choices) + self.com_sample_geo.grid(row=6, column=1, columnspan=2, padx=5, pady=5) + + # Sample Height + ttk.Label(grid_frame, text="Sample Height:").grid(row=7, column=0, sticky=tk.E) + sample_height_frame = ttk.Frame(grid_frame) + sample_height_frame.grid(row=7, column=1, columnspan=2) + self.txt_sample_height = ttk.Entry(sample_height_frame, width=5) + self.txt_sample_height.pack(side=tk.LEFT, padx=5, pady=5) + ttk.Label(sample_height_frame, text="mm").pack(side=tk.LEFT) + + # Sample Width + ttk.Label(grid_frame, text="Sample Width:").grid(row=8, column=0, sticky=tk.E) + sample_width_frame = ttk.Frame(grid_frame) + sample_width_frame.grid(row=8, column=1, columnspan=2) + self.txt_sample_width = ttk.Entry(sample_width_frame, width=5) + self.txt_sample_width.pack(side=tk.LEFT, padx=5, pady=5) + ttk.Label(sample_width_frame, text="mm").pack(side=tk.LEFT) + + # Buttons + button_frame = ttk.Frame(self) + button_frame.pack(pady=10) + self.btn_preview_script = ttk.Button(button_frame, text="Preview Script", command=self.btn_preview_script_click) + self.btn_preview_script.pack(side=tk.LEFT, padx=5) + self.btn_write_script = ttk.Button(button_frame, text="Write Script", command=self.btn_write_script_click) + self.btn_write_script.pack(side=tk.LEFT, padx=5) + + def btn_preview_script_click(self): + if self.on_preview_clicked is not None: + self.on_preview_clicked() + + def btn_write_script_click(self): + if self.on_write_clicked is not None: + self.on_write_clicked() + +class SettingsPanelVM: + def __init__(self): + self.order_choices = ["Choice1", "Choice2", "Choice3"] + self.geometry_choices = ["Geometry1", "Geometry2", "Geometry3"] + +if __name__ == "__main__": + root = tk.Tk() + panel = SettingsPanel(root) + panel.pack() + root.mainloop() +``` + +In this Python version: +- The `SettingsPanel` class inherits from `ttk.Frame`. +- The `SettingsPanelVM` class is a simple ViewModel to provide choices for the comboboxes. +- The `create_widgets` method sets up the UI elements similar to the XAML layout. +- The `btn_preview_script_click` and `btn_write_script_click` methods handle button clicks. +- The `if __name__ == "__main__":` block creates a simple application to demonstrate the `SettingsPanel` usage. + +dehoni: Please translate the selected code into python and use approbriate packages. + +GitHub Copilot: The provided XML file is a project user file for a Visual Studio project. In Python, we can use the `xml.etree.ElementTree` module to create a similar XML structure. Here is the translated code: + +```python + + +import xml.etree.ElementTree as ET + +root = ET.Element("Project", ToolsVersion="14.0", xmlns="http://schemas.microsoft.com/developer/msbuild/2003") + +property_group = ET.SubElement(root, "PropertyGroup") +ET.SubElement(property_group, "PublishUrlHistory").text = "publish\\" +ET.SubElement(property_group, "InstallUrlHistory") +ET.SubElement(property_group, "SupportUrlHistory") +ET.SubElement(property_group, "UpdateUrlHistory") +ET.SubElement(property_group, "BootstrapperUrlHistory") +ET.SubElement(property_group, "ErrorReportUrlHistory") +ET.SubElement(property_group, "FallbackCulture").text = "en-US" +ET.SubElement(property_group, "VerifyUploadedFiles").text = "false" + +tree = ET.ElementTree(root) +tree.write("SANS_Script_GUI_v4.xml", encoding="utf-8", xml_declaration=True) +``` + +This Python script uses the `xml.etree.ElementTree` module to create an XML structure similar to the provided `.csproj.user` file. The resulting XML file will be saved as `SANS_Script_GUI_v4.xml`. + +dehoni: Please translate the selected code into python and use approbriate packages. + +GitHub Copilot: Here is the translated code into Python using the `xml.etree.ElementTree` module: + +```python + + +import xml.etree.ElementTree as ET + +root = ET.Element("Project", ToolsVersion="12.0", DefaultTargets="Build", xmlns="http://schemas.microsoft.com/developer/msbuild/2003") + +ET.SubElement(root, "Import", Project="$(MSBuildExtensionsPath)\\$(MSBuildToolsVersion)\\Microsoft.Common.props", Condition="Exists('$(MSBuildExtensionsPath)\\$(MSBuildToolsVersion)\\Microsoft.Common.props')") + +property_group = ET.SubElement(root, "PropertyGroup") +ET.SubElement(property_group, "Configuration", Condition=" '$(Configuration)' == '' ").text = "Debug" +ET.SubElement(property_group, "Platform", Condition=" '$(Platform)' == '' ").text = "AnyCPU" +ET.SubElement(property_group, "ProjectGuid").text = "{5DBFF2C3-05A0-4B4B-9080-8224D86A7B40}" +ET.SubElement(property_group, "OutputType").text = "WinExe" +ET.SubElement(property_group, "AppDesignerFolder").text = "Properties" +ET.SubElement(property_group, "RootNamespace").text = "LOQ_Script_Gui" +ET.SubElement(property_group, "AssemblyName").text = "SANS_Script_Gui_v4" +ET.SubElement(property_group, "TargetFrameworkVersion").text = "v4.8" +ET.SubElement(property_group, "FileAlignment").text = "512" +ET.SubElement(property_group, "ProjectTypeGuids").text = "{60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}" +ET.SubElement(property_group, "WarningLevel").text = "4" +ET.SubElement(property_group, "IsWebBootstrapper").text = "false" +ET.SubElement(property_group, "TargetFrameworkProfile") +ET.SubElement(property_group, "PublishUrl").text = "publish\\" +ET.SubElement(property_group, "Install").text = "true" +ET.SubElement(property_group, "InstallFrom").text = "Disk" +ET.SubElement(property_group, "UpdateEnabled").text = "false" +ET.SubElement(property_group, "UpdateMode").text = "Foreground" +ET.SubElement(property_group, "UpdateInterval").text = "7" +ET.SubElement(property_group, "UpdateIntervalUnits").text = "Days" +ET.SubElement(property_group, "UpdatePeriodically").text = "false" +ET.SubElement(property_group, "UpdateRequired").text = "false" +ET.SubElement(property_group, "MapFileExtensions").text = "true" +ET.SubElement(property_group, "ApplicationRevision").text = "0" +ET.SubElement(property_group, "ApplicationVersion").text = "1.0.0.%2a" +ET.SubElement(property_group, "UseApplicationTrust").text = "false" +ET.SubElement(property_group, "BootstrapperEnabled").text = "true" + +debug_property_group = ET.SubElement(root, "PropertyGroup", Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ") +ET.SubElement(debug_property_group, "PlatformTarget").text = "x64" +ET.SubElement(debug_property_group, "DebugSymbols").text = "true" +ET.SubElement(debug_property_group, "DebugType").text = "full" +ET.SubElement(debug_property_group, "Optimize").text = "false" +ET.SubElement(debug_property_group, "OutputPath").text = "bin\\Debug\\" +ET.SubElement(debug_property_group, "DefineConstants").text = "DEBUG;TRACE" +ET.SubElement(debug_property_group, "ErrorReport").text = "prompt" +ET.SubElement(debug_property_group, "WarningLevel").text = "4" +ET.SubElement(debug_property_group, "Prefer32Bit").text = "false" + +release_property_group = ET.SubElement(root, "PropertyGroup", Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ") +ET.SubElement(release_property_group, "PlatformTarget").text = "x64" +ET.SubElement(release_property_group, "DebugType").text = "pdbonly" +ET.SubElement(release_property_group, "Optimize").text = "true" +ET.SubElement(release_property_group, "OutputPath").text = "bin\\Release\\" +ET.SubElement(release_property_group, "DefineConstants").text = "TRACE" +ET.SubElement(release_property_group, "ErrorReport").text = "prompt" +ET.SubElement(release_property_group, "WarningLevel").text = "4" + +icon_property_group = ET.SubElement(root, "PropertyGroup") +ET.SubElement(icon_property_group, "ApplicationIcon").text = "SANS_Icon.ico" + +references = [ + ("Microsoft.Office.Interop.Excel, Version=15.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c, processorArchitecture=MSIL", "True"), + "ReachFramework", + "System", + "System.Data", + "System.Xml", + "Microsoft.CSharp", + "System.Core", + "System.Xml.Linq", + "System.Data.DataSetExtensions", + ("System.Xaml", "4.0"), + "WindowsBase", + "PresentationCore", + "PresentationFramework" +] + +item_group = ET.SubElement(root, "ItemGroup") +for ref in references: + if isinstance(ref, tuple): + reference = ET.SubElement(item_group, "Reference", Include=ref[0]) + ET.SubElement(reference, "EmbedInteropTypes").text = ref[1] + else: + ET.SubElement(item_group, "Reference", Include=ref) + +compile_files = [ + "Models\\DataCollection.cs", + "Models\\InstrumentInfo.cs", + "IO\\CsvExporter.cs", + "Dialogs\\PreviewDialog.xaml.cs", + "IO\\ExcelIO.cs", + "Models\\A2Setting.cs", + "Models\\Estimation.cs", + "Models\\Geometry.cs", + "Models\\RunOrder.cs", + "EstimationPanel.xaml.cs", + "Models\\Experiment.cs", + "Models\\ExperimentSettings.cs", + "Models\\WaitForUnits.cs", + "Scripting\\GeniePythonScriptGenerator.cs", + "Scripting\\IScriptGenerator.cs", + "Scripting\\OpenGenieScriptGenerator.cs", + "SettingsPanel.xaml.cs", + "ViewModels\\DataGridVM.cs", + "ViewModels\\EstimationPanelVM.cs", + "ViewModels\\InstrumentVM.cs", + "ViewModels\\SettingsPanelVM.cs", + "App.xaml.cs", + "MainWindow.xaml.cs", + "Properties\\AssemblyInfo.cs", + "Properties\\Resources.Designer.cs", + "Properties\\Settings.Designer.cs" +] + +for file in compile_files: + compile = ET.SubElement(item_group, "Compile", Include=file) + if file.endswith(".xaml.cs"): + ET.SubElement(compile, "DependentUpon").text = file.replace(".cs", "") + elif file.endswith(".Designer.cs"): + ET.SubElement(compile, "AutoGen").text = "True" + ET.SubElement(compile, "DesignTime").text = "True" + ET.SubElement(compile, "DependentUpon").text = file.replace(".Designer.cs", ".resx") + if "Settings" in file: + ET.SubElement(compile, "DesignTimeSharedInput").text = "True" + +pages = [ + "Dialogs\\PreviewDialog.xaml", + "EstimationPanel.xaml", + "MainWindow.xaml", + "SettingsPanel.xaml" +] + +for page in pages: + page_element = ET.SubElement(item_group, "Page", Include=page) + ET.SubElement(page_element, "SubType").text = "Designer" + ET.SubElement(page_element, "Generator").text = "MSBuild:Compile" + +embedded_resources = [ + "Properties\\Resources.resx" +] + +for resource in embedded_resources: + embedded_resource = ET.SubElement(item_group, "EmbeddedResource", Include=resource) + ET.SubElement(embedded_resource, "Generator").text = "ResXFileCodeGenerator" + ET.SubElement(embedded_resource, "LastGenOutput").text = resource.replace(".resx", ".Designer.cs") + +none_files = [ + "Properties\\Settings.settings", + "App.config" +] + +for none_file in none_files: + none = ET.SubElement(item_group, "None", Include=none_file) + if none_file.endswith(".settings"): + ET.SubElement(none, "Generator").text = "SettingsSingleFileGenerator" + ET.SubElement(none, "LastGenOutput").text = none_file.replace(".settings", ".Designer.cs") + +ET.SubElement(item_group, "AppDesigner", Include="Properties\\") + +bootstrapper_packages = [ + (".NETFramework,Version=v4.5", "Microsoft .NET Framework 4.5 %28x86 and x64%29", "true"), + ("Microsoft.Net.Framework.3.5.SP1", ".NET Framework 3.5 SP1", "false") +] + +for package in bootstrapper_packages: + bootstrapper_package = ET.SubElement(item_group, "BootstrapperPackage", Include=package[0]) + ET.SubElement(bootstrapper_package, "Visible").text = "False" + ET.SubElement(bootstrapper_package, "ProductName").text = package[1] + ET.SubElement(bootstrapper_package, "Install").text = package[2] + +ET.SubElement(item_group, "Resource", Include="SANS_Icon.ico") + +ET.SubElement(item_group, "WCFMetadata", Include="Connected Services\\") + +ET.SubElement(root, "Import", Project="$(MSBuildToolsPath)\\Microsoft.CSharp.targets") + +tree = ET.ElementTree(root) +tree.write("SANS_Script_GUI_v4.xml", encoding="utf-8", xml_declaration=True) +``` + +This Python script uses the `xml.etree.ElementTree` module to create an XML structure similar to the provided `.csproj` file. The resulting XML file will be saved as `SANS_Script_GUI_v4.xml`. + +dehoni: Please translate the selected code into python and use approbriate packages. + +GitHub Copilot: Here is the translated code into Python using the `tkinter` package: + +```python + + +import tkinter as tk +from tkinter import ttk, filedialog, messagebox +from tkinter.scrolledtext import ScrolledText + +class MainWindow(tk.Tk): + def __init__(self): + super().__init__() + + self.title("SANS Script GUI") + self.geometry("800x600") + + self.data = DataGridVM() + self.inst_info = InstrumentInfo() + self.settings = SettingsPanelVM(self.inst_info) + self.estimate = Estimation() + self.instrument = InstrumentVM(self.settings.experiment, self.estimate) + + self.create_widgets() + self.load_last_instrument() + + def create_widgets(self): + self.data_grid = ttk.Treeview(self) + self.data_grid.pack(fill=tk.BOTH, expand=True) + + self.pnl_settings = SettingsPanel(self, self.settings) + self.pnl_settings.pack(fill=tk.X) + self.pnl_settings.on_preview_clicked = self.pnl_settings_on_preview_clicked + self.pnl_settings.on_write_clicked = self.pnl_settings_on_write_clicked + + self.pnl_estimate = EstimationPanel(self, self.estimate) + self.pnl_estimate.pack(fill=tk.X) + self.pnl_estimate.on_calculate_clicked = self.pnl_estimate_on_calculate_clicked + + self.btn_create_new_table = ttk.Button(self, text="Create New Table", command=self.btn_create_new_table_click) + self.btn_create_new_table.pack(side=tk.LEFT, padx=5, pady=5) + + self.btn_open_excel = ttk.Button(self, text="Open Excel", command=self.btn_open_excel_click) + self.btn_open_excel.pack(side=tk.LEFT, padx=5, pady=5) + + self.mnu_main = tk.Menu(self) + self.config(menu=self.mnu_main) + + file_menu = tk.Menu(self.mnu_main, tearoff=0) + self.mnu_main.add_cascade(label="File", menu=file_menu) + file_menu.add_command(label="Export to Excel", command=self.mnu_export_to_excel_click) + file_menu.add_command(label="Save as CSV", command=self.mnu_save_csv_click) + file_menu.add_separator() + file_menu.add_command(label="Exit", command=self.mnu_exit_click) + + def load_last_instrument(self): + inst = "LARMOR" # Replace with actual settings retrieval + if inst == "LARMOR": + self.instrument.is_larmor = True + elif inst == "LOQ": + self.instrument.is_loq = True + elif inst == "SANS2D": + self.instrument.is_sans2d = True + elif inst == "ZOOM": + self.instrument.is_zoom = True + else: + self.instrument.is_larmor = True + + def pnl_settings_on_write_clicked(self): + filetypes = [("genie_python Files", "*.py")] if self.instrument.is_larmor or self.instrument.is_loq or self.instrument.is_zoom or self.instrument.is_sans2d else [("Open Genie Files", "*.gcl")] + filepath = filedialog.asksaveasfilename(defaultextension=filetypes[0][1], filetypes=filetypes) + if filepath: + scriptname = filepath.split('/')[-1].replace(" ", "_").rsplit('.', 1)[0] + script = self.inst_info.script_generator.generate_script(self.data.runs, self.settings.experiment, scriptname) + with open(filepath, 'w') as file: + file.write(script) + + def pnl_settings_on_preview_clicked(self): + script = self.inst_info.script_generator.generate_script(self.data.runs, self.settings.experiment, "my_script") + preview_dialog = PreviewDialog(self, script) + preview_dialog.show() + + def pnl_estimate_on_calculate_clicked(self): + self.estimate.script_time = self.inst_info.script_generator.estimate_run_time(self.data.runs, self.settings.experiment, self.estimate) + + def btn_create_new_table_click(self): + self.data.runs.clear() + + def btn_open_excel_click(self): + filepath = filedialog.askopenfilename(defaultextension=".xlsx", filetypes=[("Excel Files", "*.xls;*.xlsx")]) + if filepath: + try: + excel = ExcelIO() + excel.open_excel_workbook(filepath) + exps = excel.extract_experiments() + self.data.runs = exps + except Exception as err: + messagebox.showerror("SANS Script - Error", "Could not open Excel spreadsheet") + + def mnu_exit_click(self): + self.destroy() + + def mnu_export_to_excel_click(self): + excel = ExcelIO() + excel.export_data_to_excel(self.data.runs) + + def mnu_save_csv_click(self): + filepath = filedialog.asksaveasfilename(defaultextension=".csv", filetypes=[("CSV File", "*.csv")]) + if filepath: + try: + CsvExporter.export(filepath, self.data.runs) + except Exception as err: + messagebox.showerror("SANS Script - Error", "Could not save as CSV") + + def on_close(self): + if self.instrument.is_larmor: + last_instrument = "LARMOR" + elif self.instrument.is_loq: + last_instrument = "LOQ" + elif self.instrument.is_sans2d: + last_instrument = "SANS2D" + elif self.instrument.is_zoom: + last_instrument = "ZOOM" + else: + last_instrument = "LARMOR" + # Save last_instrument to settings + self.destroy() + +class SettingsPanel(ttk.Frame): + def __init__(self, parent, settings_vm): + super().__init__(parent) + self.settings_vm = settings_vm + self.on_preview_clicked = None + self.on_write_clicked = None + self.create_widgets() + + def create_widgets(self): + self.preview_button = ttk.Button(self, text="Preview Script", command=self.preview_click) + self.preview_button.pack(side=tk.LEFT, padx=5, pady=5) + self.write_button = ttk.Button(self, text="Write Script", command=self.write_click) + self.write_button.pack(side=tk.LEFT, padx=5, pady=5) + + def preview_click(self): + if self.on_preview_clicked: + self.on_preview_clicked() + + def write_click(self): + if self.on_write_clicked: + self.on_write_clicked() + +class EstimationPanel(ttk.Frame): + def __init__(self, parent, estimate): + super().__init__(parent) + self.estimate = estimate + self.on_calculate_clicked = None + self.create_widgets() + + def create_widgets(self): + self.calculate_button = ttk.Button(self, text="Calculate", command=self.calculate_click) + self.calculate_button.pack(side=tk.LEFT, padx=5, pady=5) + + def calculate_click(self): + if self.on_calculate_clicked: + self.on_calculate_clicked() + +class PreviewDialog(tk.Toplevel): + def __init__(self, parent, script): + super().__init__(parent) + self.title("Preview Script") + self.geometry("600x400") + self.script_text = ScrolledText(self) + self.script_text.pack(fill=tk.BOTH, expand=True) + self.script_text.insert(tk.END, script) + self.script_text.config(state=tk.DISABLED) + + def show(self): + self.grab_set() + self.wait_window() + +class DataGridVM: + def __init__(self): + self.runs = [] + +class SettingsPanelVM: + def __init__(self, inst_info): + self.experiment = Experiment() + self.inst_info = inst_info + +class Estimation: + def __init__(self): + self.script_time = 0 + +class InstrumentInfo: + class ScriptGenerator: + @staticmethod + def generate_script(runs, experiment, scriptname): + return f"# Script for {scriptname}\n# Runs: {runs}\n# Experiment: {experiment}" + + @staticmethod + def estimate_run_time(runs, experiment, estimate): + return len(runs) * 10 # Dummy implementation + +class InstrumentVM: + def __init__(self, experiment, estimate): + self.is_larmor = False + self.is_loq = False + self.is_sans2d = False + self.is_zoom = False + self.experiment = experiment + self.estimate = estimate + +class ExcelIO: + def open_excel_workbook(self, filename): + pass # Dummy implementation + + def extract_experiments(self): + return [] # Dummy implementation + + def export_data_to_excel(self, runs): + pass # Dummy implementation + +class CsvExporter: + @staticmethod + def export(filename, runs): + pass # Dummy implementation + +class Experiment: + pass # Dummy implementation + +if __name__ == "__main__": + app = MainWindow() + app.protocol("WM_DELETE_WINDOW", app.on_close) + app.mainloop() +``` + +In this Python version: +- The + +MainWindow + + class inherits from `tk.Tk` and sets up the main window and its widgets. +- The `SettingsPanel` and `EstimationPanel` classes are custom frames for the settings and estimation panels. +- The + +PreviewDialog + + class is a custom dialog for previewing the script. +- The + +DataGridVM + +, + +SettingsPanelVM + +, + +Estimation + +, + +InstrumentInfo + +, + +InstrumentVM + +, + +ExcelIO + +, + +CsvExporter + +, and + +Experiment + + classes are placeholders for the corresponding functionality. +- The `if __name__ == "__main__":` block creates and runs the main application window. + +dehoni: Please translate the selected code into python and use approbriate packages. + +GitHub Copilot: Here is the translated code into Python using the `tkinter` package: + +```python + + +import tkinter as tk +from tkinter import ttk, Menu, filedialog, messagebox + +class MainWindow(tk.Tk): + def __init__(self): + super().__init__() + + self.title("SANS Script") + self.geometry("1400x650") + + self.create_widgets() + + def create_widgets(self): + self.grid_columnconfigure(1, weight=1) + self.grid_rowconfigure(4, weight=1) + + # Menu + self.mnu_main = Menu(self) + self.config(menu=self.mnu_main) + + file_menu = Menu(self.mnu_main, tearoff=0) + self.mnu_main.add_cascade(label="_File", menu=file_menu) + file_menu.add_command(label="Export Table to Excel", command=self.mnu_export_to_excel_click) + file_menu.add_command(label="Save as CSV", command=self.mnu_save_csv_click) + file_menu.add_separator() + file_menu.add_command(label="E_xit", command=self.mnu_exit_click) + + instrument_menu = Menu(self.mnu_main, tearoff=0) + self.mnu_main.add_cascade(label="_Instrument", menu=instrument_menu) + self.is_larmor = tk.BooleanVar() + self.is_loq = tk.BooleanVar() + self.is_sans2d = tk.BooleanVar() + self.is_zoom = tk.BooleanVar() + instrument_menu.add_checkbutton(label="LARMOR", variable=self.is_larmor) + instrument_menu.add_checkbutton(label="LOQ", variable=self.is_loq) + instrument_menu.add_checkbutton(label="SANS2D", variable=self.is_sans2d) + instrument_menu.add_checkbutton(label="ZOOM", variable=self.is_zoom) + + # Buttons + button_frame = ttk.Frame(self) + button_frame.grid(row=1, column=0, columnspan=3, pady=10) + self.btn_open_excel = ttk.Button(button_frame, text="Load Excel File", command=self.btn_open_excel_click) + self.btn_open_excel.pack(side=tk.LEFT, padx=5) + self.btn_create_new_table = ttk.Button(button_frame, text="Create New Table", command=self.btn_create_new_table_click) + self.btn_create_new_table.pack(side=tk.LEFT, padx=5) + + # Settings Panel + self.pnl_settings = SettingsPanel(self) + self.pnl_settings.grid(row=2, column=0, columnspan=3, sticky="ew") + + # Estimation Panel + self.pnl_estimate = EstimationPanel(self) + self.pnl_estimate.grid(row=3, column=0, columnspan=3, sticky="ew") + + # Data Grid + self.data_grid = ttk.Treeview(self, columns=("POSITION", "TRANS", "TRANS__WAIT", "SANS", "SANS__WAIT", "PERIOD", "SAMPLE__IDENTIFICATION__STRING", "BACKGROUND", "THICKNESS", "TEMPERATURE", "TEMPERATURE2", "FIELD", "SHEAR__RATE__1", "SHEAR__RATE__2", "SHEAR__ANGLE__1", "SHEAR__ANGLE__2", "PRE__COMMAND", "POST__COMMAND", "RB__NUMBER"), show="headings") + self.data_grid.grid(row=1, column=1, rowspan=4, columnspan=2, sticky="nsew", padx=5, pady=5) + for col in self.data_grid["columns"]: + self.data_grid.heading(col, text=col.replace("__", " ")) + + def btn_open_excel_click(self): + filepath = filedialog.askopenfilename(defaultextension=".xlsx", filetypes=[("Excel Files", "*.xls;*.xlsx")]) + if filepath: + try: + excel = ExcelIO() + excel.open_excel_workbook(filepath) + exps = excel.extract_experiments() + self.data_grid.delete(*self.data_grid.get_children()) + for exp in exps: + self.data_grid.insert("", "end", values=exp) + except Exception as err: + messagebox.showerror("SANS Script - Error", "Could not open Excel spreadsheet") + + def btn_create_new_table_click(self): + self.data_grid.delete(*self.data_grid.get_children()) + + def mnu_export_to_excel_click(self): + excel = ExcelIO() + excel.export_data_to_excel(self.data_grid.get_children()) + + def mnu_save_csv_click(self): + filepath = filedialog.asksaveasfilename(defaultextension=".csv", filetypes=[("CSV File", "*.csv")]) + if filepath: + try: + CsvExporter.export(filepath, self.data_grid.get_children()) + except Exception as err: + messagebox.showerror("SANS Script - Error", "Could not save as CSV") + + def mnu_exit_click(self): + self.destroy() + +class SettingsPanel(ttk.Frame): + def __init__(self, parent): + super().__init__(parent) + self.create_widgets() + + def create_widgets(self): + self.preview_button = ttk.Button(self, text="Preview Script", command=self.preview_click) + self.preview_button.pack(side=tk.LEFT, padx=5, pady=5) + self.write_button = ttk.Button(self, text="Write Script", command=self.write_click) + self.write_button.pack(side=tk.LEFT, padx=5, pady=5) + + def preview_click(self): + pass # Implement preview click functionality + + def write_click(self): + pass # Implement write click functionality + +class EstimationPanel(ttk.Frame): + def __init__(self, parent): + super().__init__(parent) + self.create_widgets() + + def create_widgets(self): + self.calculate_button = ttk.Button(self, text="Calculate", command=self.calculate_click) + self.calculate_button.pack(side=tk.LEFT, padx=5, pady=5) + + def calculate_click(self): + pass # Implement calculate click functionality + +class ExcelIO: + def open_excel_workbook(self, filename): + pass # Dummy implementation + + def extract_experiments(self): + return [] # Dummy implementation + + def export_data_to_excel(self, runs): + pass # Dummy implementation + +class CsvExporter: + @staticmethod + def export(filename, runs): + pass # Dummy implementation + +if __name__ == "__main__": + app = MainWindow() + app.mainloop() +``` + +In this Python version: +- The `MainWindow` class inherits from `tk.Tk` and sets up the main window and its widgets. +- The `SettingsPanel` and `EstimationPanel` classes are custom frames for the settings and estimation panels. +- The `ExcelIO` and `CsvExporter` classes are placeholders for the corresponding functionality. +- The `if __name__ == "__main__":` block creates and runs the main application window. + +dehoni: Please translate the selected code into python and use approbriate packages. + +GitHub Copilot: Here is the translated code into Python using the `tkinter` package: + +```python + + +import tkinter as tk +from tkinter import ttk + +class EstimationPanel(ttk.Frame): + def __init__(self, parent): + super().__init__(parent) + self.on_calculate_clicked = None + self.create_widgets() + + def create_widgets(self): + self.calculate_button = ttk.Button(self, text="Calculate", command=self.btn_calculate_click) + self.calculate_button.pack(padx=5, pady=5) + + def btn_calculate_click(self): + if self.on_calculate_clicked is not None: + self.on_calculate_clicked() + +if __name__ == "__main__": + root = tk.Tk() + panel = EstimationPanel(root) + panel.pack() + root.mainloop() +``` + +In this Python version: +- The + +EstimationPanel + + class inherits from `ttk.Frame`. +- The `on_calculate_clicked` attribute is used to store a callback function. +- The `create_widgets` method sets up the UI elements. +- The `btn_calculate_click` method handles the button click event and calls the callback if it is set. +- The `if __name__ == "__main__":` block creates a simple application to demonstrate the + +EstimationPanel + + usage. + +dehoni: Please translate the selected code into python and use approbriate packages. + +GitHub Copilot: Here is the translated code into Python using the `tkinter` package: + +```python + + +import tkinter as tk +from tkinter import ttk + +class EstimationPanel(ttk.Frame): + def __init__(self, parent, view_model): + super().__init__(parent) + self.view_model = view_model + self.on_calculate_clicked = None + self.create_widgets() + + def create_widgets(self): + self.pack(fill=tk.BOTH, expand=True) + + label = ttk.Label(self, text="Estimated Script Time", font=("Helvetica", 16, "bold")) + label.pack(pady=10) + + grid_frame = ttk.Frame(self) + grid_frame.pack(padx=10, pady=10) + + # Est. count rate + ttk.Label(grid_frame, text="Est. count rate:").grid(row=0, column=0, sticky=tk.E) + count_rate_frame = ttk.Frame(grid_frame) + count_rate_frame.grid(row=0, column=1, columnspan=2) + self.txt_count_rate = ttk.Entry(count_rate_frame, width=10, textvariable=self.view_model.count_rate) + self.txt_count_rate.pack(side=tk.LEFT, padx=5, pady=5) + ttk.Label(count_rate_frame, text="µA/hr").pack(side=tk.LEFT) + + # Est. time between moves + ttk.Label(grid_frame, text="Est. time between moves:").grid(row=1, column=0, sticky=tk.E) + move_time_frame = ttk.Frame(grid_frame) + move_time_frame.grid(row=1, column=1, columnspan=2) + self.txt_move_time = ttk.Entry(move_time_frame, width=10, textvariable=self.view_model.move_time) + self.txt_move_time.pack(side=tk.LEFT, padx=5, pady=5) + ttk.Label(move_time_frame, text="seconds").pack(side=tk.LEFT) + + # Est. script time + ttk.Label(grid_frame, text="Est. script time:").grid(row=2, column=0, sticky=tk.E) + script_time_frame = ttk.Frame(grid_frame) + script_time_frame.grid(row=2, column=1, columnspan=2) + self.txt_estimate = ttk.Entry(script_time_frame, width=15, textvariable=self.view_model.script_time, state='readonly') + self.txt_estimate.pack(side=tk.LEFT, padx=5, pady=5) + + # Calculate button + self.btn_calculate = ttk.Button(grid_frame, text="Calculate", command=self.btn_calculate_click) + self.btn_calculate.grid(row=3, column=1, columnspan=2, pady=10) + + def btn_calculate_click(self): + if self.on_calculate_clicked is not None: + self.on_calculate_clicked() + +class EstimationPanelViewModel: + def __init__(self): + self.count_rate = tk.StringVar() + self.move_time = tk.StringVar() + self.script_time = tk.StringVar() + +if __name__ == "__main__": + root = tk.Tk() + view_model = EstimationPanelViewModel() + panel = EstimationPanel(root, view_model) + panel.pack(fill=tk.BOTH, expand=True) + root.mainloop() +``` + +In this Python version: +- The `EstimationPanel` class inherits from `ttk.Frame`. +- The `EstimationPanelViewModel` class is used to bind data to the UI elements. +- The `create_widgets` method sets up the UI elements similar to the XAML layout. +- The `btn_calculate_click` method handles the button click event and calls the callback if it is set. +- The `if __name__ == "__main__":` block creates a simple application to demonstrate the `EstimationPanel` usage. + +dehoni: Please translate the selected code into python and use approbriate packages. + +GitHub Copilot: Here is the translated code into Python using the `tkinter` package: + +```python + + +import tkinter as tk +from MainWindow import MainWindow + +class App: + def __init__(self): + self.root = tk.Tk() + self.main_window = MainWindow(self.root) + + def run(self): + self.root.mainloop() + +if __name__ == "__main__": + app = App() + app.run() +``` + +In this Python version: +- The + +App + + class initializes the main application window using `tkinter`. +- The `run` method starts the main event loop. +- The `if __name__ == "__main__":` block creates an instance of the + +App + + class and runs the application. + +dehoni: Please translate the selected code into python and use approbriate packages. + +GitHub Copilot: Here is the translated code into Python using the `tkinter` package: + +```python + + +import tkinter as tk +from MainWindow import MainWindow + +class App: + def __init__(self): + self.root = tk.Tk() + self.main_window = MainWindow(self.root) + self.main_window.pack(fill=tk.BOTH, expand=True) + + def run(self): + self.root.mainloop() + +if __name__ == "__main__": + app = App() + app.run() +``` + +In this Python version: +- The `App` class initializes the main application window using `tkinter`. +- The `MainWindow` class is imported and instantiated, and it is packed to fill the main window. +- The `run` method starts the main event loop. +- The `if __name__ == "__main__":` block creates an instance of the `App` class and runs the application. + +dehoni: Please translate the selected code into python and use approbriate packages. + +GitHub Copilot: Here is the translated code into Python using the `configparser` package: + +```python + + +import configparser + +config = configparser.ConfigParser() + +config['DEFAULT'] = { + 'LastInstrument': 'LARMOR', + 'SampleHeight': '11', + 'SampleWidth': '11', + 'Geometry': 'Flat Plate', + 'RunOrder': 'Alternate - TRANS first', + 'NumTrans': '1', + 'NumSans': '1', + 'LoopPerRun': 'False', + 'A2SettingSans': 'Large = 25, 16 mm', + 'A2SettingTrans': 'Large = 25, 16 mm', + 'CollectionMode': 'Events' +} + +with open('app_config.ini', 'w') as configfile: + config.write(configfile) +``` + +In this Python version: +- The `configparser` module is used to create and manage the configuration settings. +- The settings from the XML file are added to the `DEFAULT` section of the configuration. +- The configuration is written to a file named `app_config.ini`. + +dehoni: Please translate the selected code into python and use approbriate packages. + +GitHub Copilot: Here is the translated code into Python using the `tkinter` package and the `tkinter` variable classes for data binding: + +```python + + +import tkinter as tk +from tkinter import StringVar +from tkinter import ttk + +class DataGridVM: + def __init__(self): + self._runs = tk.Variable(value=[]) + self.property_changed = [] + + @property + def runs(self): + return self._runs.get() + + @runs.setter + def runs(self, value): + self._runs.set(value) + self.on_property_changed("runs") + + @staticmethod + def wait_for_choices(): + return WaitForUnits.choices() + + def on_property_changed(self, name): + for callback in self.property_changed: + callback(name) + +class WaitForUnits: + @staticmethod + def choices(): + return ["Choice1", "Choice2", "Choice3"] + +class Experiment: + pass # Dummy implementation + +if __name__ == "__main__": + root = tk.Tk() + vm = DataGridVM() + + def on_property_changed(name): + print(f"Property {name} changed") + + vm.property_changed.append(on_property_changed) + + vm.runs = [Experiment(), Experiment()] + print(vm.runs) + + root.mainloop() +``` + +In this Python version: +- The + +DataGridVM + + class uses `tk.Variable` for data binding. +- The + +runs + + property is implemented with getter and setter methods to handle property changes. +- The `on_property_changed` method notifies subscribers when a property changes. +- The + +WaitForUnits + + class provides static choices for the `wait_for_choices` method. +- The + +Experiment + + class is a placeholder for the actual implementation. +- The `if __name__ == "__main__":` block demonstrates how to use the + +DataGridVM + + class and handle property changes. + +dehoni: Please translate the selected code into python and use approbriate packages. + +GitHub Copilot: Here is the translated code into Python using the `tkinter` package and the `tkinter` variable classes for data binding: + +```python + + +import tkinter as tk + +class EstimationPanelVM: + def __init__(self): + self.property_changed = [] + + def on_property_changed(self, name): + for callback in self.property_changed: + callback(name) + +if __name__ == "__main__": + root = tk.Tk() + vm = EstimationPanelVM() + + def on_property_changed(name): + print(f"Property {name} changed") + + vm.property_changed.append(on_property_changed) + + # Example of triggering property change + vm.on_property_changed("example_property") + + root.mainloop() +``` + +In this Python version: +- The + +EstimationPanelVM + + class uses a list to store property change callbacks. +- The `on_property_changed` method notifies subscribers when a property changes. +- The `if __name__ == "__main__":` block demonstrates how to use the + +EstimationPanelVM + + class and handle property changes. + +dehoni: Please translate the selected code into python and use approbriate packages. + +GitHub Copilot: Here is the translated code into Python using the `tkinter` package and the `tkinter` variable classes for data binding: + +```python + + +import tkinter as tk +from tkinter import StringVar, BooleanVar + +class InstrumentVM: + def __init__(self, settings, estimation): + self.settings = settings + self.estimation = estimation + + self.loq_aperture_sizes = [A2Setting.Large, A2Setting.Medium, A2Setting.Small] + self.sans2d_aperture_sizes = [A2Setting.XLarge, A2Setting.Large, A2Setting.Medium, A2Setting.Small] + self.larmor_aperture_sizes = [A2Setting.Larmor] + self.zoom_aperture_sizes = [A2Setting.Large, A2Setting.Medium, A2Setting.Small] + + self.is_larmor = BooleanVar(value=True) + self.is_loq = BooleanVar(value=False) + self.is_sans2d = BooleanVar(value=False) + self.is_zoom = BooleanVar(value=False) + + self.is_larmor.trace_add("write", self.update_instrument) + self.is_loq.trace_add("write", self.update_instrument) + self.is_sans2d.trace_add("write", self.update_instrument) + self.is_zoom.trace_add("write", self.update_instrument) + + self.update_instrument() + + def update_instrument(self, *args): + if self.is_larmor.get(): + self.set_instrument(True, False, False, False, "A1, S1 Setting:", self.larmor_aperture_sizes, 40) + elif self.is_loq.get(): + self.set_instrument(False, True, False, False, "A2 Setting:", self.loq_aperture_sizes, 170) + elif self.is_sans2d.get(): + self.set_instrument(False, False, True, False, "A6 Setting:", self.sans2d_aperture_sizes, 40) + elif self.is_zoom.get(): + self.set_instrument(False, False, False, True, "A6 Setting:", self.zoom_aperture_sizes, 40) + + def set_instrument(self, larmor, loq, sans2d, zoom, a2_label, a2_choices, count_rate): + self.is_larmor.set(larmor) + self.is_loq.set(loq) + self.is_sans2d.set(sans2d) + self.is_zoom.set(zoom) + + InstrumentInfo.script_generator = Scripting.GeniePythonScriptGenerator(larmor, loq, zoom, sans2d) + InstrumentInfo.a2_label = a2_label + InstrumentInfo.a2_choices = a2_choices + InstrumentInfo.a2_trans_enabled = True + InstrumentInfo.collection_mode_enabled = True + self.estimation.count_rate = count_rate + self.settings.a2_setting_sans = Properties.Settings.Default.a2_setting_sans + self.settings.a2_setting_trans = Properties.Settings.Default.a2_setting_trans + +class A2Setting: + Large = "Large" + Medium = "Medium" + Small = "Small" + XLarge = "XLarge" + Larmor = "Larmor" + +class InstrumentInfo: + script_generator = None + a2_label = "" + a2_choices = [] + a2_trans_enabled = False + collection_mode_enabled = False + +class Scripting: + class GeniePythonScriptGenerator: + def __init__(self, larmor, loq, zoom, sans2d): + pass # Dummy implementation + +class Properties: + class Settings: + class Default: + a2_setting_sans = "Large" + a2_setting_trans = "Large" + +class ExperimentSettings: + def __init__(self): + self.a2_setting_sans = "" + self.a2_setting_trans = "" + +class Estimation: + def __init__(self): + self.count_rate = 0 + +if __name__ == "__main__": + root = tk.Tk() + settings = ExperimentSettings() + estimation = Estimation() + vm = InstrumentVM(settings, estimation) + + def on_property_changed(name): + print(f"Property {name} changed") + + vm.is_larmor.trace_add("write", lambda *args: on_property_changed("is_larmor")) + vm.is_loq.trace_add("write", lambda *args: on_property_changed("is_loq")) + vm.is_sans2d.trace_add("write", lambda *args: on_property_changed("is_sans2d")) + vm.is_zoom.trace_add("write", lambda *args: on_property_changed("is_zoom")) + + root.mainloop() +``` + +In this Python version: +- The + +InstrumentVM + + class uses `BooleanVar` for data binding. +- The `update_instrument` method updates the instrument settings based on the selected instrument. +- The `set_instrument` method sets the instrument-specific properties. +- The + +A2Setting + +, + +InstrumentInfo + +, + +Scripting + +, + +Properties + +, + +ExperimentSettings + +, and + +Estimation + + classes are placeholders for the actual implementation. +- The `if __name__ == "__main__":` block demonstrates how to use the + +InstrumentVM + + class and handle property changes. + +dehoni: Please translate the selected code into python and use approbriate packages. + +GitHub Copilot: Here is the translated code into Python using the `tkinter` package and the `tkinter` variable classes for data binding: + +```python + + +import tkinter as tk +from tkinter import StringVar + +class SettingsPanelVM: + def __init__(self, inst_info=None): + self.property_changed = [] + + self._experiment = ExperimentSettings() + self._inst_info = inst_info if inst_info else InstrumentInfo() + + def on_property_changed(self, name): + for callback in self.property_changed: + callback(name) + + @property + def experiment(self): + return self._experiment + + @experiment.setter + def experiment(self, value): + self._experiment = value + self.on_property_changed("experiment") + + @property + def inst_info(self): + return self._inst_info + + @inst_info.setter + def inst_info(self, value): + self._inst_info = value + self.on_property_changed("inst_info") + + @staticmethod + def order_choices(): + return RunOrder.choices() + + @staticmethod + def geometry_choices(): + return Geometry.choices() + + @staticmethod + def collection_mode_choices(): + return DataCollection.choices() + +class RunOrder: + @staticmethod + def choices(): + return ["Choice1", "Choice2", "Choice3"] + +class Geometry: + @staticmethod + def choices(): + return ["Geometry1", "Geometry2", "Geometry3"] + +class DataCollection: + @staticmethod + def choices(): + return ["Mode1", "Mode2", "Mode3"] + +class ExperimentSettings: + pass # Dummy implementation + +class InstrumentInfo: + pass # Dummy implementation + +if __name__ == "__main__": + root = tk.Tk() + vm = SettingsPanelVM() + + def on_property_changed(name): + print(f"Property {name} changed") + + vm.property_changed.append(on_property_changed) + + vm.experiment = ExperimentSettings() + vm.inst_info = InstrumentInfo() + + root.mainloop() +``` + +In this Python version: +- The + +SettingsPanelVM + + class uses a list to store property change callbacks. +- The `on_property_changed` method notifies subscribers when a property changes. +- The + +experiment + + and `inst_info` properties are implemented with getter and setter methods to handle property changes. +- The `order_choices`, `geometry_choices`, and `collection_mode_choices` methods provide static choices for the combo boxes. +- The + +RunOrder + +, + +Geometry + +, + +DataCollection + +, + +ExperimentSettings + +, and + +InstrumentInfo + + classes are placeholders for the actual implementation. +- The `if __name__ == "__main__":` block demonstrates how to use the + +SettingsPanelVM + + class and handle property changes. + +dehoni: Please translate the selected code into python and use approbriate packages. + +GitHub Copilot: Here is the translated code into Python: + +```python + + +from datetime import datetime, timedelta + +class GeniePythonScriptGenerator: + def __init__(self, is_larmor, is_loq, is_zoom, is_sans2d): + self.indent = " " + self.is_larmor = is_larmor + self.is_loq = is_loq + self.is_zoom = is_zoom + self.is_sans2d = is_sans2d + + def generate_script(self, runs, settings, scriptname): + script = f"# Script created by SANSScript at {datetime.now()}\n\n" + script += self.generate_header(scriptname) + script += self.generate_setup(settings) + script += self.generate_runs(runs, settings) + script += "\n" + return script + + def get_indent_string(self, n): + return self.indent * n + + def generate_header(self, scriptname): + script = "import sys\n" + script += "from genie_python import genie as g, BLOCK_NAMES as b\n" + script += "sys.path.append(r\"c:\\instrument\\scripts\")\n" + + if self.is_larmor: + script += "from instrument.larmor import * # pylint: disable=wildcard-import, unused-wildcard-import\n" + if self.is_loq: + script += "from instrument.loq import * # pylint: disable=wildcard-import, unused-wildcard-import\n" + if self.is_zoom: + script += "from instrument.zoom import * # pylint: disable=wildcard-import, unused-wildcard-import\n" + if self.is_sans2d: + script += "from instrument.sans2d import * # pylint: disable=wildcard-import, unused-wildcard-import\n" + + script += f"\ndef {scriptname}():\n" + return script + + def generate_setup(self, settings): + script = "" + script += self.get_indent_string(1) + f"g.change_sample_par('Width', '{settings.sample_width}')\n" + script += self.get_indent_string(1) + f"g.change_sample_par('height', '{settings.sample_height}')\n" + script += self.get_indent_string(1) + f"g.change_sample_par('Geometry', '{settings.sample_geometry}')\n\n" + return script + + def check_positions(self, runs): + script = "" + if self.is_larmor: + position_list = ",".join([exp.position for exp in runs if exp.position]) + if position_list: + script += self.get_indent_string(1) + f"if check_move_pos(['{position_list}']) != True:\n" + script += self.get_indent_string(2) + "print('Invalid positions - script aborted')\n" + script += self.get_indent_string(2) + "return\n\n" + return script + + def generate_runs(self, runs, settings): + script = self.check_positions(runs) + if not runs: + return script + + if settings.order == "AllTrans": + script += self.get_indent_string(1) + f"for i in range({settings.num_trans}):\n" + for exp in runs: + if exp.trans: + script = self.do_trans_run(script, exp, 2, settings) + script += self.get_indent_string(1) + f"for i in range({settings.num_sans}):\n" + for exp in runs: + if exp.sans: + script = self.do_sans_run(script, exp, 2, settings) + elif settings.order == "AllSans": + script += self.get_indent_string(1) + f"for i in range({settings.num_sans}):\n" + for exp in runs: + if exp.sans: + script = self.do_sans_run(script, exp, 2, settings) + if settings.num_trans and any(exp.trans for exp in runs): + script += self.get_indent_string(1) + f"for i in range({settings.num_trans}):\n" + for exp in runs: + if exp.trans: + script = self.do_trans_run(script, exp, 2, settings) + else: + script += self.get_indent_string(1) + f"num_sans = {settings.num_sans}\n" + script += self.get_indent_string(1) + f"num_trans = {settings.num_trans}\n" + script += self.get_indent_string(1) + "count = 0\n" + script += self.get_indent_string(1) + "while True:\n" + indent = 2 + for exp in runs: + if settings.order == "TransFirst": + if exp.trans: + script += self.get_indent_string(indent) + "if count < num_trans:\n" + script = self.do_trans_run(script, exp, indent + 1, settings) + if exp.sans: + script += self.get_indent_string(indent) + "if count < num_sans:\n" + script = self.do_sans_run(script, exp, indent + 1, settings) + else: + if exp.sans: + script += self.get_indent_string(indent) + "if count < num_sans:\n" + script = self.do_sans_run(script, exp, indent + 1, settings) + if exp.trans: + script += self.get_indent_string(indent) + "if count < num_trans:\n" + script = self.do_trans_run(script, exp, indent + 1, settings) + script += self.get_indent_string(indent) + "count += 1\n" + script += self.get_indent_string(indent) + "if count >= num_trans and count >= num_sans: break\n" + return script + + def do_trans_run(self, script, exp, indent, settings): + script += self.do_sample_environment(exp, indent) + waitfor = "uamps" + rtype = "histogram" + fulltitle = f"{{{exp.sample}}}" + if settings.collection_mode == "Events": + rtype = "event" + if exp.trans_wait == "Frames": + waitfor = "frames" + elif exp.trans_wait == "Seconds": + waitfor = "seconds" + elif exp.trans_wait == "Minutes": + waitfor = "minutes" + if self.is_larmor or self.is_zoom: + script += self.get_indent_string(indent) + f"do_trans(title='{fulltitle}', pos='{exp.position}', {waitfor}={exp.trans}, thickness={exp.thickness})\n" + elif self.is_loq: + script += self.get_indent_string(indent) + f"do_trans(title='{fulltitle}', pos='{exp.position}', {waitfor}={exp.trans}, thickness={exp.thickness}, aperture='small')\n" + else: + script += self.get_indent_string(indent) + f"do_trans(title='{fulltitle}', pos='{exp.position}', {waitfor}={exp.trans}, thickness={exp.thickness}, dae='transmission', aperture='{self.aperture_string(settings.a2_setting_trans)}')\n" + script += self.get_indent_string(indent) + exp.post_command + "\n" + return script + + def do_sans_run(self, script, exp, indent, settings): + script += self.do_sample_environment(exp, indent) + waitfor = "uamps" + rtype = "histogram" + fulltitle = f"{{{exp.sample}}}" + if exp.background: + fulltitle += f"_{{{exp.background}}}" + if settings.collection_mode in ["Events", "TRANSSANS_Events"]: + rtype = "event" + elif settings.collection_mode == "SESANS": + rtype = "sesans" + if settings.collection_mode in ["TRANSSANS_Events", "TRANSSANS_Histogram"]: + fulltitle += "_TRANS" + if exp.sans_wait == "Frames": + waitfor = "frames" + elif exp.sans_wait == "Seconds": + waitfor = "seconds" + elif exp.sans_wait == "Minutes": + waitfor = "minutes" + if self.is_larmor or self.is_zoom: + script += self.get_indent_string(indent) + f"do_sans(title='{fulltitle}', pos='{exp.position}', {waitfor}={exp.sans}, thickness={exp.thickness}, dae='{rtype}')\n" + else: + script += self.get_indent_string(indent) + f"do_sans(title='{fulltitle}', pos='{exp.position}', {waitfor}={exp.sans}, thickness={exp.thickness}, dae='{rtype}', aperture='{self.aperture_string(settings.a2_setting_sans)}')\n" + script += self.get_indent_string(indent) + exp.post_command + "\n" + return script + + def aperture_string(self, size): + if size in [A2Setting.Large, A2Setting.Larmor]: + return "large" + elif size in [A2Setting.Medium, A2Setting.Larmor]: + return "medium" + elif size in [A2Setting.Small, A2Setting.Larmor]: + return "small" + elif size in [A2Setting.XLarge, A2Setting.Larmor]: + return "xlarge" + return "" + + def do_sample_environment(self, exp, indent): + script = "" + if exp.temperature1: + temp1 = float(exp.temperature1) + temp1low = temp1 - 1.0 + temp1high = temp1 + 1.0 + if self.is_loq: + script += self.get_indent_string(indent) + f"g.cset(internal_setpoint_1={exp.temperature1})\n" + script += self.get_indent_string(indent) + f"g.cset(\"Julabo_1_ext\", lowlimit={temp1low}, highlimit={temp1high}, runcontrol=True)\n" + else: + script += self.get_indent_string(indent) + f"g.cset(JULABO1_SP={exp.temperature1})\n" + script += self.get_indent_string(indent) + f"g.cset(\"JULABO1_TEMP\", lowlimit={temp1low}, highlimit={temp1high}, runcontrol=True)\n" + if exp.temperature2: + temp2 = float(exp.temperature2) + temp2low = temp2 - 1.0 + temp2high = temp2 + 1.0 + if self.is_loq: + script += self.get_indent_string(indent) + f"g.cset(internal_setpoint_2={exp.temperature2})\n" + script += self.get_indent_string(indent) + f"g.cset(\"Julabo_2_ext\", lowlimit={temp2low}, highlimit={temp2high}, runcontrol=True)\n" + else: + script += self.get_indent_string(indent) + f"g.cset(JULABO2_SP={exp.temperature2})\n" + script += self.get_indent_string(indent) + f"g.cset(\"JULABO2_TEMP\", lowlimit={temp2low}, highlimit={temp2high}, runcontrol=True)\n" + if exp.pre_command: + script += self.get_indent_string(indent) + exp.pre_command + "\n" + if exp.field: + script += self.get_indent_string(indent) + f"g.cset(FIELD={exp.field})\n" + if exp.shear_rate1: + script += self.get_indent_string(indent) + f"g.cset(SHEAR_RATE_1={exp.shear_rate1})\n" + if exp.shear_rate2: + script += self.get_indent_string(indent) + f"g.cset(SHEAR_RATE_2={exp.shear_rate2})\n" + if exp.shear_angle1: + script += self.get_indent_string(indent) + f"g.cset(SHEAR_ANGLE_1={exp.shear_angle1})\n" + if exp.shear_angle2: + script += self.get_indent_string(indent) + f"g.cset(SHEAR_ANGLE_2={exp.shear_angle2})\n" + return script + + def estimate_run_time(self, runs, settings, estimation): + minutes = 0 + if settings.num_sans > 0: + minutes += self.calculate_time(runs, True, estimation.count_rate, estimation.move_time) * settings.num_sans + if settings.num_trans > 0: + minutes += self.calculate_time(runs, False, estimation.count_rate, estimation.move_time) * settings.num_trans + time = datetime(1, 1, 1, 0, 0, 0) + timedelta(minutes=minutes) + return time.strftime("%H:%M:%S") + + @staticmethod + def calculate_time(runs, is_sans, count_rate, seconds_between): + minutes = 0 + for exp in runs: + if exp.position: + if is_sans: + minutes += GeniePythonScriptGenerator.get_minutes(exp.sans_wait, exp.sans, count_rate) + else: + minutes += GeniePythonScriptGenerator.get_minutes(exp.trans_wait, exp.trans, count_rate) + minutes += seconds_between / 60 + return minutes + + @staticmethod + def get_minutes(wait, length, count_rate): + if wait == "Minutes": + return length + elif wait == "Seconds": + return length / 60.0 + elif wait == "Frames": + return length / (25.0 * 60.0) + else: + return length / (count_rate / 60.0) + +class A2Setting: + Large = "Large" + Medium = "Medium" + Small = "Small" + XLarge = "XLarge" + Larmor = "Larmor" + +class Experiment: + def __init__(self, position="", trans=0, sans=0, trans_wait="", sans_wait="", sample="", background="", thickness=0, pre_command="", post_command="", temperature1="", temperature2="", field="", shear_rate1="", shear_rate2="", shear_angle1="", shear_angle2=""): + self.position = position + self.trans = trans + self.sans = sans + self.trans_wait = trans_wait + self.sans_wait = sans_wait + self.sample = sample + self.background = background + self.thickness = thickness + self.pre_command = pre_command + self.post_command = post_command + self.temperature1 = temperature1 + self.temperature2 = temperature2 + self.field = field + self.shear_rate1 = shear_rate1 + self.shear_rate2 = shear_rate2 + self.shear_angle1 = shear_angle1 + self.shear_angle2 = shear_angle2 + +class ExperimentSettings: + def __init__(self, sample_width="", sample_height="", sample_geometry="", order="", num_trans=0, num_sans=0, collection_mode="", a2_setting_trans="", a2_setting_sans=""): + self.sample_width = sample_width + self.sample_height = sample_height + self.sample_geometry = sample_geometry + self.order = order + self.num_trans = num_trans + self.num_sans = num_sans + self.collection_mode = collection_mode + self.a2_setting_trans = a2_setting_trans + self.a2_setting_sans = a2_setting_sans + +class Estimation: + def __init__(self, count_rate=0, move_time=0): + self.count_rate = count_rate + self.move_time = move_time + +if __name__ == "__main__": + runs = [Experiment(position="pos1", trans=10, sans=20, trans_wait="Minutes", sans_wait="Minutes", sample="Sample1", thickness=1.0)] + settings = ExperimentSettings(sample_width="10", sample_height="10", sample_geometry="Flat", order="AllTrans", num_trans=1, num_sans=1, collection_mode="Events", a2_setting_trans="Large", a2_setting_sans="Large") + estimation = Estimation(count_rate=100, move_time=10) + generator = GeniePythonScriptGenerator(is_larmor=True, is_loq=False, is_zoom=False, is_sans2d=False) + script = generator.generate_script(runs, settings, "my_script") + print(script) + estimated_time = generator.estimate_run_time(runs, settings, estimation) + print(f"Estimated run time: {estimated_time}") +``` + +In this Python version: +- The + +GeniePythonScriptGenerator + + class is implemented with methods to generate the script and estimate run time. +- The + +A2Setting + +, + +Experiment + +, + +ExperimentSettings + +, and + +Estimation + + classes are implemented as placeholders for the actual data structures. +- The `if __name__ == "__main__":` block demonstrates how to use the + +GeniePythonScriptGenerator + + class to generate a script and estimate run time. + +dehoni: Please translate the selected code into python and use approbriate packages. + +GitHub Copilot: Here is the translated code into Python using an abstract base class: + +```python + + +from abc import ABC, abstractmethod + +class IScriptGenerator(ABC): + @abstractmethod + def generate_script(self, runs, settings, scriptname): + pass + + @abstractmethod + def estimate_run_time(self, runs, settings, estimation): + pass + +class Experiment: + pass # Dummy implementation + +class ExperimentSettings: + pass # Dummy implementation + +class Estimation: + pass # Dummy implementation +``` + +In this Python version: +- The + +IScriptGenerator + + class is defined as an abstract base class using the `abc` module. +- The `generate_script` and `estimate_run_time` methods are defined as abstract methods. +- The + +Experiment + +, + +ExperimentSettings + +, and + +Estimation + + classes are placeholders for the actual implementations. + +dehoni: Please translate the selected code into python and use approbriate packages. + +GitHub Copilot: Here is the translated code into Python: + +```python + + +from datetime import datetime, timedelta + +class OpenGenieScriptGenerator: + def __init__(self, is_sans2d): + self.is_sans2d = is_sans2d + + def generate_script(self, runs, settings, scriptname): + script = f"# Script created by LOQ Script at {datetime.now()}\n" + script += "# Script MUST be loaded in to Open GENIE using the loadscript command.\n\n" + + if self.is_sans2d: + script += "QUALIFIERS /test\n" + + script += f"LOCAL transCount = 0 sansCount = 0 numTRANS={settings.num_trans} numSANS={settings.num_sans}\n\n" + script += "SETSCRIPTNAME THIS_PROCEDURE()\n\n" + script += f'Sample_par width={settings.sample_width} height={settings.sample_height} geometry="{settings.sample_geometry}"\n\n' + + if self.is_sans2d: + position_list = ",".join([exp.position for exp in runs if exp.position]) + if position_list: + script += f'IF NOT CHECK_MOVE_POS("{position_list}")\n' + script += ' PRINTEN "Invalid positions - script aborted"\n' + script += ' RETURN\n' + script += 'ENDIF\n\n' + script += "IF test\n" + script += " RETURN\n" + script += "ENDIF\n\n" + + if settings.order == "AllTrans": + if settings.loop_per_run: + script += self.do_all_trans_loop_each(runs, settings, True, True) + script += self.do_all_sans_loop_each(runs, settings, True, True) + else: + script += "LOOP\n\n" + script += self.do_all_trans(runs, settings, True, True) + script += self.do_all_sans(runs, settings, True, True) + script += "EXITIF ((transCount >= numTRANS) AND (sansCount >= numSANS))\n\n" + script += "ENDLOOP\n\n" + elif settings.order == "TransFirst": + if settings.loop_per_run: + for exp in runs: + script += self.do_single_trans_loop_each(exp, settings, True, False) + script += self.do_single_sans_loop_each(exp, settings, False, True) + else: + script += "LOOP\n\n" + for exp in runs: + script += self.do_single_trans(exp, settings, True, False) + script += self.do_single_sans(exp, settings, False, True) + script += "transCount = transCount + 1\n" + script += "sansCount = sansCount + 1\n\n" + script += "EXITIF ((transCount >= numTRANS) AND (sansCount >= numSANS))\n\n" + script += "ENDLOOP\n\n" + elif settings.order == "AllSans": + if settings.loop_per_run: + script += self.do_all_sans_loop_each(runs, settings, True, True) + script += self.do_all_trans_loop_each(runs, settings, True, True) + else: + script += "LOOP\n\n" + script += self.do_all_sans(runs, settings, True, True) + script += self.do_all_trans(runs, settings, True, True) + script += "EXITIF ((transCount >= numTRANS) AND (sansCount >= numSANS))\n\n" + script += "ENDLOOP\n\n" + elif settings.order == "SansFirst": + if settings.loop_per_run: + for exp in runs: + script += self.do_single_sans_loop_each(exp, settings, True, False) + script += self.do_single_trans_loop_each(exp, settings, False, True) + else: + script += "LOOP\n\n" + for exp in runs: + script += self.do_single_sans(exp, settings, True, False) + script += self.do_single_trans(exp, settings, False, True) + script += "transCount = transCount + 1\n" + script += "sansCount = sansCount + 1\n\n" + script += "EXITIF ((transCount >= numTRANS) AND (sansCount >= numSANS))\n\n" + script += "ENDLOOP\n\n" + + script += 'PRINTN "SCRIPT COMPLETED "\n\n' + return script + + def do_single_sans(self, exp, settings, do_pre_com, do_post_com): + script = "" + if settings.num_sans > 0 and exp.sans > 0: + script += "IF (sansCount < numSANS)\n" + script += self.set_sans_aperture(settings.a2_setting_sans) + script += self.do_single_run(False, exp, False, do_pre_com, do_post_com, settings) + script += "\nENDIF\n\n" + return script + + def do_single_trans(self, exp, settings, do_pre_com, do_post_com): + script = "" + if settings.num_trans > 0 and exp.trans > 0: + script += "IF (transCount < numTRANS)\n" + script += self.set_trans_aperture(settings.a2_setting_trans) + script += self.do_single_run(True, exp, False, do_pre_com, do_post_com, settings) + script += "\nENDIF\n\n" + return script + + def do_single_sans_loop_each(self, exp, settings, do_pre_com, do_post_com): + script = "" + if settings.num_sans > 0 and exp.sans > 0: + script += self.set_sans_aperture(settings.a2_setting_sans) + script += self.do_single_run(False, exp, True, do_pre_com, do_post_com, settings) + "\n" + return script + + def do_single_trans_loop_each(self, exp, settings, do_pre_com, do_post_com): + script = "" + if settings.num_trans > 0 and exp.trans > 0: + script += self.set_trans_aperture(settings.a2_setting_trans) + script += self.do_single_run(True, exp, True, do_pre_com, do_post_com, settings) + "\n" + return script + + def do_all_sans(self, runs, settings, do_pre_com, do_post_com): + script = "" + if settings.num_sans > 0: + script += self.set_sans_aperture(settings.a2_setting_sans) + script += "IF sansCount < numSANS\n\n" + for exp in runs: + script += self.do_single_run(False, exp, False, do_pre_com, do_post_com, settings) + "\n" + script += "sansCount = sansCount + 1\n\n" + script += "ENDIF\n\n" + return script + + def do_all_trans(self, runs, settings, do_pre_com, do_post_com): + script = "" + if settings.num_trans > 0: + script += self.set_trans_aperture(settings.a2_setting_trans) + script += "IF transCount < numTRANS\n\n" + for exp in runs: + script += self.do_single_run(True, exp, False, do_pre_com, do_post_com, settings) + "\n" + script += "transCount = transCount + 1\n\n" + script += "ENDIF\n\n" + return script + + def do_all_sans_loop_each(self, runs, settings, do_pre_com, do_post_com): + script = "" + if settings.num_sans > 0: + script += self.set_sans_aperture(settings.a2_setting_sans) + for exp in runs: + script += self.do_single_run(False, exp, True, do_pre_com, do_post_com, settings) + "\n" + return script + + def do_all_trans_loop_each(self, runs, settings, do_pre_com, do_post_com): + script = "" + if settings.num_trans > 0: + script += self.set_trans_aperture(settings.a2_setting_trans) + for exp in runs: + script += self.do_single_run(True, exp, True, do_pre_com, do_post_com, settings) + "\n" + return script + + def set_sans_aperture(self, a2_size_sans): + script = "" + if a2_size_sans == "Large": + script += "DO_SANS /LARGE\n\n" + elif a2_size_sans == "Medium": + script += "DO_SANS /MEDIUM\n\n" + elif a2_size_sans == "Small": + script += "DO_SANS /SMALL\n\n" + elif a2_size_sans == "Trans": + script += "DO_SANS /TRANS\n\n" + return script + + def set_trans_aperture(self, a2_size_trans): + script = "" + if not a2_size_trans: + script += "DO_TRANS\n\n" + else: + if a2_size_trans == "Large": + script += "DO_TRANS /LARGE\n\n" + elif a2_size_trans == "Medium": + script += "DO_TRANS /MEDIUM\n\n" + elif a2_size_trans == "Small": + script += "DO_TRANS /SMALL\n\n" + elif a2_size_trans == "Trans": + script += "DO_TRANS /TRANS\n\n" + return script + + def do_single_run(self, is_trans, exp, is_loop, do_pre_com, do_post_com, settings): + script = "" + if exp.trans > 0 or exp.sans > 0: + if exp.rb_number: + script += f"change rbno={exp.rb_number}\n" + if exp.position: + if do_pre_com: + script += self.do_sample_environment(exp) + if is_loop: + if is_trans: + script += "LOOP transCount FROM 1 TO numTRANS\n" + else: + script += "LOOP sansCount FROM 1 TO numSANS\n" + script += self.create_move_command(is_trans, exp, settings) + script += "ENDLOOP\n" + else: + script += self.create_move_command(is_trans, exp, settings) + if do_post_com and exp.post_command: + script += exp.post_command + "\n" + return script + + def do_sample_environment(self, exp): + script = "" + if exp.temperature1: + script += f"CSET TEMP={exp.temperature1}\n" + if exp.temperature2: + script += f"CSET TEMP2={exp.temperature2}\n" + if exp.pre_command: + script += exp.pre_command + "\n" + if exp.field: + script += f"CSET FIELD={exp.field}\n" + if exp.shear_rate1: + script += f"CSET SHEAR_RATE_1={exp.shear_rate1}\n" + if exp.shear_rate2: + script += f"CSET SHEAR_RATE_2={exp.shear_rate2}\n" + if exp.shear_angle1: + script += f"CSET SHEAR_ANGLE_1={exp.shear_angle1}\n" + if exp.shear_angle2: + script += f"CSET SHEAR_ANGLE_2={exp.shear_angle2}\n" + return script + + def create_move_command(self, is_trans, exp, settings): + script = f'MOVE pos="{exp.position}"' + fulltitle = f"{{{exp.sample}}}" + if exp.background: + fulltitle += f"_{{{exp.background}}}" + if exp.thickness: + script += f" thick={exp.thickness}" + else: + script += " thick=1" + if exp.period: + script += f" period={exp.period}" + if is_trans: + script += self.write_wait_for(exp.trans_wait, exp.trans) + if fulltitle: + script += f' title="{fulltitle}_TRANS"' + else: + script += self.write_wait_for(exp.sans_wait, exp.sans) + if fulltitle: + if settings.collection_mode in ["TRANSSANS_Events", "TRANSSANS_Histogram"]: + script += f' title="{fulltitle}_TRANS_SANS"' + else: + script += f' title="{fulltitle}_SANS"' + return script + "\n" + + def write_wait_for(self, waitfor, length): + script = "" + if waitfor: + if waitfor.lower() == "MicroAmps".lower(): + script += f" uAhr={length}" + elif waitfor.lower() == "Minutes".lower(): + script += f" min={length}" + elif waitfor.lower() == "Seconds".lower(): + script += f" seconds={length}" + elif waitfor.lower() == "Frames".lower(): + script += f" frame={length}" + else: + script += f" uAhr={length}" + else: + script += f" uAhr={length}" + return script + + def estimate_run_time(self, runs, settings, estimation): + minutes = 0 + if settings.num_sans > 0: + minutes += self.calculate_time(runs, True, estimation.count_rate, estimation.move_time) * settings.num_sans + if settings.num_trans > 0: + minutes += self.calculate_time(runs, False, estimation.count_rate, estimation.move_time) * settings.num_trans + time = datetime(1, 1, 1, 0, 0, 0) + timedelta(minutes=minutes) + return time.strftime("%H:%M:%S") + + @staticmethod + def calculate_time(runs, is_sans, count_rate, seconds_between): + minutes = 0 + for exp in runs: + if exp.position: + if is_sans: + minutes += OpenGenieScriptGenerator.get_minutes(exp.sans_wait, exp.sans, count_rate) + else: + minutes += OpenGenieScriptGenerator.get_minutes(exp.trans_wait, exp.trans, count_rate) + minutes += seconds_between / 60 + return minutes + + @staticmethod + def get_minutes(wait, length, count_rate): + if wait == "Minutes": + return length + elif wait == "Seconds": + return length / 60.0 + elif wait == "Frames": + return length / (25.0 * 60.0) + else: + return length / (count_rate / 60.0) + +class Experiment: + def __init__(self, position="", trans=0, sans=0, trans_wait="", sans_wait="", sample="", background="", thickness=0, pre_command="", post_command="", temperature1="", temperature2="", field="", shear_rate1="", shear_rate2="", shear_angle1="", shear_angle2="", rb_number="", period=""): + self.position = position + self.trans = trans + self.sans = sans + self.trans_wait = trans_wait + self.sans_wait = sans_wait + self.sample = sample + self.background = background + self.thickness = thickness + self.pre_command = pre_command + self.post_command = post_command + self.temperature1 = temperature1 + self.temperature2 = temperature2 + self.field = field + self.shear_rate1 = shear_rate1 + self.shear_rate2 = shear_rate2 + self.shear_angle1 = shear_angle1 + self.shear_angle2 = shear_angle2 + self.rb_number = rb_number + self.period = period + +class ExperimentSettings: + def __init__(self, sample_width="", sample_height="", sample_geometry="", order="", num_trans=0, num_sans=0, collection_mode="", a2_setting_trans="", a2_setting_sans="", loop_per_run=False): + self.sample_width = sample_width + self.sample_height = sample_height + self.sample_geometry = sample_geometry + self.order = order + self.num_trans = num_trans + self.num_sans = num_sans + self.collection_mode = collection_mode + self.a2_setting_trans = a2_setting_trans + self.a2_setting_sans = a2_setting_sans + self.loop_per_run = loop_per_run + +class Estimation: + def __init__(self, count_rate=0, move_time=0): + self.count_rate = count_rate + self.move_time = move_time + +if __name__ == "__main__": + runs = [Experiment(position="pos1", trans=10, sans=20, trans_wait="Minutes", sans_wait="Minutes", sample="Sample1", thickness=1.0)] + settings = ExperimentSettings(sample_width="10", sample_height="10", sample_geometry="Flat", order="AllTrans", num_trans=1, num_sans=1, collection_mode="Events", a2_setting_trans="Large", a2_setting_sans="Large", loop_per_run=False) + estimation = Estimation(count_rate=100, move_time=10) + generator = OpenGenieScriptGenerator(is_sans2d=True) + script = generator.generate_script(runs, settings, "my_script") + print(script) + estimated_time = generator.estimate_run_time(runs, settings, estimation) + print(f"Estimated run time: {estimated_time}") +``` + +In this Python version: +- The `OpenGenieScriptGenerator` class is implemented with methods to generate the script and estimate run time. +- The `Experiment`, `ExperimentSettings`, and `Estimation` classes are implemented as placeholders for the actual data structures. +- The `if __name__ == "__main__":` block demonstrates how to use the `OpenGenieScriptGenerator` class to generate a script and estimate run time. + +dehoni: Please translate the selected code into python and use approbriate packages. + +GitHub Copilot: Here is the translated code into Python using the `setuptools` package for setting up package metadata: + +```python + + +from setuptools import setup, find_packages + +setup( + name="LOQ_Script_Gui", + version="1.0.0", + description="", + author="", + author_email="", + url="", + packages=find_packages(), + include_package_data=True, + install_requires=[ + # Add your dependencies here + ], + classifiers=[ + "Programming Language :: Python :: 3", + "License :: OSI Approved :: MIT License", + "Operating System :: OS Independent", + ], + python_requires='>=3.6', +) + +# Additional metadata for localization and resource management can be added as needed. +``` + +In this Python version: +- The `setup.py` file is used to define the package metadata and dependencies. +- The `setuptools` package is used to handle the package setup. +- The `find_packages` function is used to automatically discover all packages and subpackages. +- The `install_requires` list can be populated with the required dependencies. +- The `classifiers` list is used to provide additional metadata about the package. + +dehoni: Please translate the selected code into python and use approbriate packages. + +GitHub Copilot: Here is the translated code into Python using the `gettext` package for handling localization and resource management: + +```python + + +import gettext +import os + +class Resources: + def __init__(self): + self.resource_man = None + self.resource_culture = None + + @property + def resource_manager(self): + if self.resource_man is None: + locale_dir = os.path.join(os.path.dirname(__file__), 'locale') + self.resource_man = gettext.translation('resources', localedir=locale_dir, languages=[self.resource_culture]) + return self.resource_man + + @property + def culture(self): + return self.resource_culture + + @culture.setter + def culture(self, value): + self.resource_culture = value + self.resource_man = None # Reset the resource manager to reload with the new culture + +# Example usage +resources = Resources() +resources.culture = 'en_US' # Set the desired culture +_ = resources.resource_manager.gettext + +# Example of using the localized string +print(_("Hello, World!")) +``` + +In this Python version: +- The `gettext` package is used for localization and resource management. +- The + +Resources + + class manages the resource manager and culture settings. +- The `resource_manager` property initializes the `gettext.translation` object based on the specified culture. +- The `culture` property allows setting the desired culture and resets the resource manager to reload with the new culture. +- An example usage is provided to demonstrate how to set the culture and use the localized strings. + +dehoni: Please translate the selected code into python and use approbriate packages. + +GitHub Copilot: Here is the translated code into Python using the `configparser` package for handling resource files: + +```python + + +[resheader] +resmimetype = text/microsoft-resx +version = 2.0 +reader = System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 +writer = System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + +[data] +Name1 = this is my long string +Color1 = Blue +Bitmap1 = [base64 mime encoded serialized .NET Framework object] +Icon1 = [base64 mime encoded string representing a byte array form of the .NET Framework object] + +[metadata] +Name1.comment = this is a comment +Icon1.comment = This is a comment +``` + +In this Python version: +- The `.resx` file is translated into an `.ini` file format using the `configparser` package. +- The `resheader` section contains metadata about the resource file. +- The `data` section contains the actual resource data. +- The `metadata` section contains comments associated with the data. + +To read this `.ini` file in Python, you can use the following code: + +```python + + +import configparser + +config = configparser.ConfigParser() +config.read('resources.ini') + +# Accessing resheader values +resmimetype = config['resheader']['resmimetype'] +version = config['resheader']['version'] +reader = config['resheader']['reader'] +writer = config['resheader']['writer'] + +# Accessing data values +name1 = config['data']['Name1'] +color1 = config['data']['Color1'] +bitmap1 = config['data']['Bitmap1'] +icon1 = config['data']['Icon1'] + +# Accessing metadata comments +name1_comment = config['metadata']['Name1.comment'] +icon1_comment = config['metadata']['Icon1.comment'] + +print(f"Name1: {name1} (Comment: {name1_comment})") +print(f"Color1: {color1}") +print(f"Bitmap1: {bitmap1}") +print(f"Icon1: {icon1} (Comment: {icon1_comment})") +``` + +This code reads the `resources.ini` file and prints the values and comments for the resources. + +dehoni: Please translate the selected code into python and use approbriate packages. + +GitHub Copilot: Here is the translated code into Python using the `configparser` package for handling application settings: + +```python + + +[DEFAULT] +LastInstrument = LARMOR +SampleHeight = 11 +SampleWidth = 11 +Geometry = Flat Plate +RunOrder = Alternate - TRANS first +NumTrans = 1 +NumSans = 1 +LoopPerRun = False +A2SettingSans = Large = 25, 16 mm +A2SettingTrans = Large = 25, 16 mm +CollectionMode = Events +``` + +To read and write these settings in Python, you can use the following code: + +```python + + +import configparser + +class Settings: + def __init__(self, filename='settings.ini'): + self.config = configparser.ConfigParser() + self.filename = filename + self.config.read(self.filename) + + @property + def last_instrument(self): + return self.config['DEFAULT']['LastInstrument'] + + @last_instrument.setter + def last_instrument(self, value): + self.config['DEFAULT']['LastInstrument'] = value + self.save() + + @property + def sample_height(self): + return self.config['DEFAULT']['SampleHeight'] + + @sample_height.setter + def sample_height(self, value): + self.config['DEFAULT']['SampleHeight'] = value + self.save() + + @property + def sample_width(self): + return self.config['DEFAULT']['SampleWidth'] + + @sample_width.setter + def sample_width(self, value): + self.config['DEFAULT']['SampleWidth'] = value + self.save() + + @property + def geometry(self): + return self.config['DEFAULT']['Geometry'] + + @geometry.setter + def geometry(self, value): + self.config['DEFAULT']['Geometry'] = value + self.save() + + @property + def run_order(self): + return self.config['DEFAULT']['RunOrder'] + + @run_order.setter + def run_order(self, value): + self.config['DEFAULT']['RunOrder'] = value + self.save() + + @property + def num_trans(self): + return self.config['DEFAULT']['NumTrans'] + + @num_trans.setter + def num_trans(self, value): + self.config['DEFAULT']['NumTrans'] = value + self.save() + + @property + def num_sans(self): + return self.config['DEFAULT']['NumSans'] + + @num_sans.setter + def num_sans(self, value): + self.config['DEFAULT']['NumSans'] = value + self.save() + + @property + def loop_per_run(self): + return self.config['DEFAULT']['LoopPerRun'] + + @loop_per_run.setter + def loop_per_run(self, value): + self.config['DEFAULT']['LoopPerRun'] = value + self.save() + + @property + def a2_setting_sans(self): + return self.config['DEFAULT']['A2SettingSans'] + + @a2_setting_sans.setter + def a2_setting_sans(self, value): + self.config['DEFAULT']['A2SettingSans'] = value + self.save() + + @property + def a2_setting_trans(self): + return self.config['DEFAULT']['A2SettingTrans'] + + @a2_setting_trans.setter + def a2_setting_trans(self, value): + self.config['DEFAULT']['A2SettingTrans'] = value + self.save() + + @property + def collection_mode(self): + return self.config['DEFAULT']['CollectionMode'] + + @collection_mode.setter + def collection_mode(self, value): + self.config['DEFAULT']['CollectionMode'] = value + self.save() + + def save(self): + with open(self.filename, 'w') as configfile: + self.config.write(configfile) + +# Example usage +settings = Settings() +print(settings.last_instrument) +settings.last_instrument = 'LOQ' +print(settings.last_instrument) +``` + +In this Python version: +- The `settings.ini` file is used to store the application settings. +- The + +Settings + + class provides properties to get and set the settings values. +- The `save` method writes the updated settings back to the `settings.ini` file. +- An example usage is provided to demonstrate how to read and update the settings. + +dehoni: Please translate the selected code into python and use approbriate packages. + +GitHub Copilot: Here is the translated code into Python using the `configparser` package for handling application settings: + +```python + + +[DEFAULT] +LastInstrument = LARMOR +SampleHeight = 11 +SampleWidth = 11 +Geometry = Flat Plate +RunOrder = Alternate - TRANS first +NumTrans = 1 +NumSans = 1 +LoopPerRun = False +A2SettingSans = Large = 25, 16 mm +A2SettingTrans = Large = 25, 16 mm +CollectionMode = Events +``` + +To read and write these settings in Python, you can use the following code: + +```python + + +import configparser + +class Settings: + def __init__(self, filename='settings.ini'): + self.config = configparser.ConfigParser() + self.filename = filename + self.config.read(self.filename) + + @property + def last_instrument(self): + return self.config['DEFAULT']['LastInstrument'] + + @last_instrument.setter + def last_instrument(self, value): + self.config['DEFAULT']['LastInstrument'] = value + self.save() + + @property + def sample_height(self): + return self.config['DEFAULT']['SampleHeight'] + + @sample_height.setter + def sample_height(self, value): + self.config['DEFAULT']['SampleHeight'] = value + self.save() + + @property + def sample_width(self): + return self.config['DEFAULT']['SampleWidth'] + + @sample_width.setter + def sample_width(self, value): + self.config['DEFAULT']['SampleWidth'] = value + self.save() + + @property + def geometry(self): + return self.config['DEFAULT']['Geometry'] + + @geometry.setter + def geometry(self, value): + self.config['DEFAULT']['Geometry'] = value + self.save() + + @property + def run_order(self): + return self.config['DEFAULT']['RunOrder'] + + @run_order.setter + def run_order(self, value): + self.config['DEFAULT']['RunOrder'] = value + self.save() + + @property + def num_trans(self): + return self.config['DEFAULT']['NumTrans'] + + @num_trans.setter + def num_trans(self, value): + self.config['DEFAULT']['NumTrans'] = value + self.save() + + @property + def num_sans(self): + return self.config['DEFAULT']['NumSans'] + + @num_sans.setter + def num_sans(self, value): + self.config['DEFAULT']['NumSans'] = value + self.save() + + @property + def loop_per_run(self): + return self.config['DEFAULT']['LoopPerRun'] + + @loop_per_run.setter + def loop_per_run(self, value): + self.config['DEFAULT']['LoopPerRun'] = value + self.save() + + @property + def a2_setting_sans(self): + return self.config['DEFAULT']['A2SettingSans'] + + @a2_setting_sans.setter + def a2_setting_sans(self, value): + self.config['DEFAULT']['A2SettingSans'] = value + self.save() + + @property + def a2_setting_trans(self): + return self.config['DEFAULT']['A2SettingTrans'] + + @a2_setting_trans.setter + def a2_setting_trans(self, value): + self.config['DEFAULT']['A2SettingTrans'] = value + self.save() + + @property + def collection_mode(self): + return self.config['DEFAULT']['CollectionMode'] + + @collection_mode.setter + def collection_mode(self, value): + self.config['DEFAULT']['CollectionMode'] = value + self.save() + + def save(self): + with open(self.filename, 'w') as configfile: + self.config.write(configfile) + +# Example usage +settings = Settings() +print(settings.last_instrument) +settings.last_instrument = 'LOQ' +print(settings.last_instrument) +``` + +In this Python version: +- The `settings.ini` file is used to store the application settings. +- The `Settings` class provides properties to get and set the settings values. +- The `save` method writes the updated settings back to the `settings.ini` file. +- An example usage is provided to demonstrate how to read and update the settings. + +dehoni: Please translate the selected code into python and use approbriate packages. + +GitHub Copilot: Here is the translated code into Python: + +```python + + +class A2Setting: + X_LARGE = "XLarge = 20x18 mm" + LARGE = "Large = 17 mm" + MEDIUM = "Medium = 12 mm" + SMALL = "Small = 5/1 mm" + TRANS = "Trans = 1 mm" + LARMOR = "" + # LARMOR_LARGE = "Large = 25, 16 mm" + # LARMOR_MEDIUM = "Medium = 20, 14 mm" + # LARMOR_SMALL = "Small = 15, 10 mm" + +# Example usage +if __name__ == "__main__": + print(A2Setting.X_LARGE) + print(A2Setting.LARGE) + print(A2Setting.MEDIUM) + print(A2Setting.SMALL) + print(A2Setting.TRANS) + print(A2Setting.LARMOR) +``` + +In this Python version: +- The + +A2Setting + + class is defined with class attributes for each setting. +- The commented-out settings from the original C# code are also commented out in the Python version. +- An example usage is provided to demonstrate how to access the settings. + +dehoni: Please translate the selected code into python and use approbriate packages. + +GitHub Copilot: Here is the translated code into Python: + +```python + + +class DataCollection: + HISTOGRAM = "Histogram" + EVENTS = "Events" + SESANS = "SESANS" + TRANSSANS_EVENTS = "TRANSSANS_Events" + TRANSSANS_HISTOGRAM = "TRANSSANS_Histogram" + + choices = [ + HISTOGRAM, + TRANSSANS_HISTOGRAM, + EVENTS, + TRANSSANS_EVENTS, + SESANS + ] + + @staticmethod + def get_choices(): + return DataCollection.choices + +# Example usage +if __name__ == "__main__": + print(DataCollection.get_choices()) +``` + +In this Python version: +- The + +DataCollection + + class is defined with class attributes for each data collection type. +- The + +choices + + list contains all the data collection types. +- The `get_choices` static method returns the list of choices. +- An example usage is provided to demonstrate how to access the choices. + +dehoni: Please translate the selected code into python and use approbriate packages. + +GitHub Copilot: Here is the translated code into Python using the `tkinter` package for data binding and validation: + +```python + + +import tkinter as tk + +class Estimation: + def __init__(self): + self._count_rate = tk.DoubleVar(value=40) + self._move_time = tk.DoubleVar(value=120) + self._script_time = tk.StringVar() + self._is_valid = tk.BooleanVar(value=True) + self.property_changed = [] + + self._count_rate.trace_add("write", self.validate) + self._move_time.trace_add("write", self.validate) + + @property + def count_rate(self): + return self._count_rate.get() + + @count_rate.setter + def count_rate(self, value): + self._count_rate.set(value) + self.on_property_changed("count_rate") + + @property + def move_time(self): + return self._move_time.get() + + @move_time.setter + def move_time(self, value): + self._move_time.set(value) + self.on_property_changed("move_time") + + @property + def script_time(self): + return self._script_time.get() + + @script_time.setter + def script_time(self, value): + self._script_time.set(value) + self.on_property_changed("script_time") + + @property + def is_valid(self): + return self._is_valid.get() + + @is_valid.setter + def is_valid(self, value): + self._is_valid.set(value) + self.on_property_changed("is_valid") + + def on_property_changed(self, name): + for callback in self.property_changed: + callback(name) + + def validate(self, *args): + error_message = "" + if self.count_rate < 0: + error_message = "Count rate cannot be negative!" + elif self.move_time < 0: + error_message = "Move time cannot be negative!" + + if error_message: + self.is_valid = False + else: + self.is_valid = True + + return error_message + +# Example usage +if __name__ == "__main__": + def on_property_changed(name): + print(f"Property {name} changed") + + estimation = Estimation() + estimation.property_changed.append(on_property_changed) + + print(estimation.count_rate) + estimation.count_rate = -10 # This should trigger validation and set is_valid to False + print(estimation.is_valid) + estimation.count_rate = 50 # This should trigger validation and set is_valid to True + print(estimation.is_valid) +``` + +In this Python version: +- The + +Estimation + + class uses `tkinter` variable classes (`DoubleVar`, `StringVar`, `BooleanVar`) for data binding. +- The `validate` method checks the validity of `count_rate` and `move_time` and updates the `is_valid` property accordingly. +- The `on_property_changed` method notifies subscribers when a property changes. +- An example usage is provided to demonstrate how to use the + +Estimation + + class and handle property changes. + +dehoni: Please translate the selected code into python and use approbriate packages. + +GitHub Copilot: Here is the translated code into Python using the `tkinter` package for data binding and property change notifications: + +```python + + +import tkinter as tk + +class Experiment: + def __init__(self): + self._position = tk.StringVar() + self._trans = tk.DoubleVar(value=0) + self._trans_wait = tk.StringVar() + self._sans = tk.DoubleVar(value=0) + self._sans_wait = tk.StringVar() + self._period = tk.StringVar() + self._background = tk.StringVar() + self._sample = tk.StringVar() + self._thickness = tk.StringVar() + self._temperature1 = tk.StringVar() + self._temperature2 = tk.StringVar() + self._field = tk.StringVar() + self._shear_rate1 = tk.StringVar() + self._shear_rate2 = tk.StringVar() + self._shear_angle1 = tk.StringVar() + self._shear_angle2 = tk.StringVar() + self._pre_command = tk.StringVar() + self._post_command = tk.StringVar() + self._rb_number = tk.StringVar() + self.property_changed = [] + + self._position.trace_add("write", self.on_property_changed) + self._trans.trace_add("write", self.on_property_changed) + self._trans_wait.trace_add("write", self.on_property_changed) + self._sans.trace_add("write", self.on_property_changed) + self._sans_wait.trace_add("write", self.on_property_changed) + self._period.trace_add("write", self.on_property_changed) + self._background.trace_add("write", self.on_property_changed) + self._sample.trace_add("write", self.on_property_changed) + self._thickness.trace_add("write", self.on_property_changed) + self._temperature1.trace_add("write", self.on_property_changed) + self._temperature2.trace_add("write", self.on_property_changed) + self._field.trace_add("write", self.on_property_changed) + self._shear_rate1.trace_add("write", self.on_property_changed) + self._shear_rate2.trace_add("write", self.on_property_changed) + self._shear_angle1.trace_add("write", self.on_property_changed) + self._shear_angle2.trace_add("write", self.on_property_changed) + self._pre_command.trace_add("write", self.on_property_changed) + self._post_command.trace_add("write", self.on_property_changed) + self._rb_number.trace_add("write", self.on_property_changed) + + def on_property_changed(self, *args): + for callback in self.property_changed: + callback(args[0]) + + @property + def position(self): + return self._position.get() + + @position.setter + def position(self, value): + self._position.set(value) + + @property + def trans(self): + return self._trans.get() + + @trans.setter + def trans(self, value): + self._trans.set(value) + + @property + def trans_wait(self): + return self._trans_wait.get() + + @trans_wait.setter + def trans_wait(self, value): + self._trans_wait.set(value) + + @property + def sans(self): + return self._sans.get() + + @sans.setter + def sans(self, value): + self._sans.set(value) + + @property + def sans_wait(self): + return self._sans_wait.get() + + @sans_wait.setter + def sans_wait(self, value): + self._sans_wait.set(value) + + @property + def period(self): + return self._period.get() + + @period.setter + def period(self, value): + self._period.set(value) + + @property + def background(self): + return self._background.get() + + @background.setter + def background(self, value): + self._background.set(value) + + @property + def sample(self): + return self._sample.get() + + @sample.setter + def sample(self, value): + self._sample.set(value) + + @property + def thickness(self): + return self._thickness.get() + + @thickness.setter + def thickness(self, value): + self._thickness.set(value) + + @property + def temperature1(self): + return self._temperature1.get() + + @temperature1.setter + def temperature1(self, value): + self._temperature1.set(value) + + @property + def temperature2(self): + return self._temperature2.get() + + @temperature2.setter + def temperature2(self, value): + self._temperature2.set(value) + + @property + def field(self): + return self._field.get() + + @field.setter + def field(self, value): + self._field.set(value) + + @property + def shear_rate1(self): + return self._shear_rate1.get() + + @shear_rate1.setter + def shear_rate1(self, value): + self._shear_rate1.set(value) + + @property + def shear_rate2(self): + return self._shear_rate2.get() + + @shear_rate2.setter + def shear_rate2(self, value): + self._shear_rate2.set(value) + + @property + def shear_angle1(self): + return self._shear_angle1.get() + + @shear_angle1.setter + def shear_angle1(self, value): + self._shear_angle1.set(value) + + @property + def shear_angle2(self): + return self._shear_angle2.get() + + @shear_angle2.setter + def shear_angle2(self, value): + self._shear_angle2.set(value) + + @property + def pre_command(self): + return self._pre_command.get() + + @pre_command.setter + def pre_command(self, value): + self._pre_command.set(value) + + @property + def post_command(self): + return self._post_command.get() + + @post_command.setter + def post_command(self, value): + self._post_command.set(value) + + @property + def rb_number(self): + return self._rb_number.get() + + @rb_number.setter + def rb_number(self, value): + self._rb_number.set(value) + +# Example usage +if __name__ == "__main__": + def on_property_changed(name): + print(f"Property {name} changed") + + experiment = Experiment() + experiment.property_changed.append(on_property_changed) + + experiment.position = "New Position" + experiment.trans = 10.5 + experiment.sans = 20.0 + experiment.temperature1 = "25" + experiment.temperature2 = "30" + experiment.field = "1.5T" + experiment.shear_rate1 = "100" + experiment.shear_rate2 = "200" + experiment.shear_angle1 = "45" + experiment.shear_angle2 = "90" + experiment.pre_command = "Pre Command" + experiment.post_command = "Post Command" + experiment.rb_number = "RB12345" +``` + +In this Python version: +- The + +Experiment + + class uses `tkinter` variable classes (`StringVar`, `DoubleVar`) for data binding. +- The `on_property_changed` method notifies subscribers when a property changes. +- Properties are defined for each attribute to get and set the values. +- An example usage is provided to demonstrate how to use the + +Experiment + + class and handle property changes. + +dehoni: Please translate the selected code into python and use approbriate packages. + +GitHub Copilot: Here is the translated code into Python using the `tkinter` package for data binding and property change notifications: + +```python + + +import tkinter as tk +from configparser import ConfigParser + +class ExperimentSettings: + def __init__(self, config_file='settings.ini'): + self.config = ConfigParser() + self.config.read(config_file) + self.config_file = config_file + + self._order = tk.StringVar(value=self.config['DEFAULT'].get('RunOrder', 'TransFirst')) + self._num_sans = tk.IntVar(value=self.config['DEFAULT'].getint('NumSans', 1)) + self._num_trans = tk.IntVar(value=self.config['DEFAULT'].getint('NumTrans', 1)) + self._loop_per_run = tk.BooleanVar(value=self.config['DEFAULT'].getboolean('LoopPerRun', False)) + self._a2_setting_sans = tk.StringVar(value=self.config['DEFAULT'].get('A2SettingSans', 'Large')) + self._a2_setting_trans = tk.StringVar(value=self.config['DEFAULT'].get('A2SettingTrans', 'Large')) + self._sample_geometry = tk.StringVar(value=self.config['DEFAULT'].get('Geometry', 'Flat Plate')) + self._sample_height = tk.DoubleVar(value=self.config['DEFAULT'].getfloat('SampleHeight', 11)) + self._sample_width = tk.DoubleVar(value=self.config['DEFAULT'].getfloat('SampleWidth', 11)) + self._sample_width_enabled = tk.BooleanVar(value=True) + self._collection_mode = tk.StringVar(value=self.config['DEFAULT'].get('CollectionMode', 'Events')) + self._is_valid = tk.BooleanVar(value=True) + self.property_changed = [] + + self._order.trace_add("write", self.on_property_changed) + self._num_sans.trace_add("write", self.on_property_changed) + self._num_trans.trace_add("write", self.on_property_changed) + self._loop_per_run.trace_add("write", self.on_property_changed) + self._a2_setting_sans.trace_add("write", self.on_property_changed) + self._a2_setting_trans.trace_add("write", self.on_property_changed) + self._sample_geometry.trace_add("write", self.on_property_changed) + self._sample_height.trace_add("write", self.on_property_changed) + self._sample_width.trace_add("write", self.on_property_changed) + self._collection_mode.trace_add("write", self.on_property_changed) + + def on_property_changed(self, *args): + for callback in self.property_changed: + callback(args[0]) + self.save() + + def save(self): + self.config['DEFAULT']['RunOrder'] = self._order.get() + self.config['DEFAULT']['NumSans'] = str(self._num_sans.get()) + self.config['DEFAULT']['NumTrans'] = str(self._num_trans.get()) + self.config['DEFAULT']['LoopPerRun'] = str(self._loop_per_run.get()) + self.config['DEFAULT']['A2SettingSans'] = self._a2_setting_sans.get() + self.config['DEFAULT']['A2SettingTrans'] = self._a2_setting_trans.get() + self.config['DEFAULT']['Geometry'] = self._sample_geometry.get() + self.config['DEFAULT']['SampleHeight'] = str(self._sample_height.get()) + self.config['DEFAULT']['SampleWidth'] = str(self._sample_width.get()) + self.config['DEFAULT']['CollectionMode'] = self._collection_mode.get() + with open(self.config_file, 'w') as configfile: + self.config.write(configfile) + + @property + def order(self): + return self._order.get() + + @order.setter + def order(self, value): + self._order.set(value) + + @property + def num_sans(self): + return self._num_sans.get() + + @num_sans.setter + def num_sans(self, value): + self._num_sans.set(value) + + @property + def num_trans(self): + return self._num_trans.get() + + @num_trans.setter + def num_trans(self, value): + self._num_trans.set(value) + + @property + def loop_per_run(self): + return self._loop_per_run.get() + + @loop_per_run.setter + def loop_per_run(self, value): + self._loop_per_run.set(value) + + @property + def a2_setting_sans(self): + return self._a2_setting_sans.get() + + @a2_setting_sans.setter + def a2_setting_sans(self, value): + self._a2_setting_sans.set(value) + + @property + def a2_setting_trans(self): + return self._a2_setting_trans.get() + + @a2_setting_trans.setter + def a2_setting_trans(self, value): + self._a2_setting_trans.set(value) + + @property + def sample_geometry(self): + return self._sample_geometry.get() + + @sample_geometry.setter + def sample_geometry(self, value): + self._sample_geometry.set(value) + self.sample_width_enabled = value != 'Disc' + + @property + def sample_height(self): + return self._sample_height.get() + + @sample_height.setter + def sample_height(self, value): + self._sample_height.set(value) + if not self.sample_width_enabled: + self.sample_width = value + + @property + def sample_width(self): + return self._sample_width.get() + + @sample_width.setter + def sample_width(self, value): + self._sample_width.set(value) + + @property + def sample_width_enabled(self): + return self._sample_width_enabled.get() + + @sample_width_enabled.setter + def sample_width_enabled(self, value): + self._sample_width_enabled.set(value) + if not value: + self.sample_width = self.sample_height + + @property + def collection_mode(self): + return self._collection_mode.get() + + @collection_mode.setter + def collection_mode(self, value): + self._collection_mode.set(value) + + @property + def is_valid(self): + return self._is_valid.get() + + @is_valid.setter + def is_valid(self, value): + self._is_valid.set(value) + + def validate(self): + error_message = "" + if self.num_sans < 0: + error_message = "Number of SANS runs cannot be negative!" + elif self.num_trans < 0: + error_message = "Number of TRANS runs cannot be negative!" + elif self.sample_height <= 0 or self.sample_height > 20: + error_message = "Sample height must be between 0 and 20" + elif self.sample_width <= 0 or self.sample_width > 20: + error_message = "Sample width must be between 0 and 20" + + if error_message: + self.is_valid = False + else: + self.is_valid = True + + return error_message + +# Example usage +if __name__ == "__main__": + def on_property_changed(name): + print(f"Property {name} changed") + + settings = ExperimentSettings() + settings.property_changed.append(on_property_changed) + + settings.order = "SansFirst" + settings.num_sans = 2 + settings.num_trans = 3 + settings.sample_height = 15 + settings.sample_width = 10 + settings.collection_mode = "Histogram" + print(settings.validate()) +``` + +In this Python version: +- The + +ExperimentSettings + + class uses `tkinter` variable classes (`StringVar`, `IntVar`, `DoubleVar`, `BooleanVar`) for data binding. +- The `on_property_changed` method notifies subscribers when a property changes and saves the settings to the `settings.ini` file. +- Properties are defined for each attribute to get and set the values. +- The `validate` method checks the validity of the settings and updates the `is_valid` property accordingly. +- An example usage is provided to demonstrate how to use the + +ExperimentSettings + + class and handle property changes. + +dehoni: Please translate the selected code into python and use approbriate packages. + +GitHub Copilot: Here is the translated code into Python: + +```python + + +class Geometry: + DISC = "Disc" + CYLINDRICAL = "Cylindrical" + FLAT_PLATE = "Flat Plate" + SINGLE_CRYSTAL = "Single Crystal" + + choices = [ + DISC, + CYLINDRICAL, + FLAT_PLATE, + SINGLE_CRYSTAL + ] + + @staticmethod + def get_choices(): + return Geometry.choices + +# Example usage +if __name__ == "__main__": + print(Geometry.get_choices()) +``` + +In this Python version: +- The + +Geometry + + class is defined with class attributes for each geometry type. +- The + +choices + + list contains all the geometry types. +- The `get_choices` static method returns the list of choices. +- An example usage is provided to demonstrate how to access the choices. + +dehoni: Please translate the selected code into python and use approbriate packages. + +GitHub Copilot: Here is the translated code into Python using the `tkinter` package for data binding and property change notifications: + +```python + + +import tkinter as tk +from tkinter import StringVar, BooleanVar +from typing import List + +class InstrumentInfo: + property_changed = [] + static_property_changed = [] + + def __init__(self): + self._a2_label = StringVar(value="A2 Setting:") + self._a2_choices = tk.Variable(value=["Large", "Medium", "Small"]) + self._a2_trans_enabled = BooleanVar(value=True) + self._collection_mode_enabled = BooleanVar(value=False) + self._script_generator = None + + self._a2_label.trace_add("write", self.on_property_changed) + self._a2_choices.trace_add("write", self.on_property_changed) + self._a2_trans_enabled.trace_add("write", self.on_property_changed) + self._collection_mode_enabled.trace_add("write", self.on_property_changed) + + def on_property_changed(self, *args): + for callback in self.property_changed: + callback(args[0]) + + @staticmethod + def on_static_property_changed(property_name): + for callback in InstrumentInfo.static_property_changed: + callback(property_name) + + @property + def a2_label(self): + return self._a2_label.get() + + @a2_label.setter + def a2_label(self, value): + self._a2_label.set(value) + self.on_static_property_changed("a2_label") + + @property + def a2_choices(self): + return self._a2_choices.get() + + @a2_choices.setter + def a2_choices(self, value: List[str]): + self._a2_choices.set(value) + self.on_static_property_changed("a2_choices") + + @property + def a2_trans_enabled(self): + return self._a2_trans_enabled.get() + + @a2_trans_enabled.setter + def a2_trans_enabled(self, value): + self._a2_trans_enabled.set(value) + self.on_static_property_changed("a2_trans_enabled") + + @property + def collection_mode_enabled(self): + return self._collection_mode_enabled.get() + + @collection_mode_enabled.setter + def collection_mode_enabled(self, value): + self._collection_mode_enabled.set(value) + self.on_static_property_changed("collection_mode_enabled") + + @property + def script_generator(self): + return self._script_generator + + @script_generator.setter + def script_generator(self, value): + self._script_generator = value + +# Example usage +if __name__ == "__main__": + def on_property_changed(name): + print(f"Property {name} changed") + + def on_static_property_changed(name): + print(f"Static property {name} changed") + + instrument_info = InstrumentInfo() + instrument_info.property_changed.append(on_property_changed) + InstrumentInfo.static_property_changed.append(on_static_property_changed) + + instrument_info.a2_label = "New A2 Setting:" + instrument_info.a2_choices = ["XLarge", "Large", "Medium", "Small"] + instrument_info.a2_trans_enabled = False + instrument_info.collection_mode_enabled = True +``` + +In this Python version: +- The + +InstrumentInfo + + class uses `tkinter` variable classes (`StringVar`, `BooleanVar`) for data binding. +- The `on_property_changed` method notifies subscribers when a property changes. +- The `on_static_property_changed` method notifies subscribers when a static property changes. +- Properties are defined for each attribute to get and set the values. +- An example usage is provided to demonstrate how to use the + +InstrumentInfo + + class and handle property changes. + +dehoni: Please translate the selected code into python and use approbriate packages. + +GitHub Copilot: Here is the translated code into Python: + +```python + + +class RunOrder: + ALL_TRANS = "All TRANS first" + TRANS_FIRST = "Alternate - TRANS first" + ALL_SANS = "All SANS first" + SANS_FIRST = "Alternate - SANS first" + + choices = [ + ALL_TRANS, + TRANS_FIRST, + ALL_SANS, + SANS_FIRST + ] + + @staticmethod + def get_choices(): + return RunOrder.choices + +# Example usage +if __name__ == "__main__": + print(RunOrder.get_choices()) +``` + +In this Python version: +- The + +RunOrder + + class is defined with class attributes for each run order type. +- The + +choices + + list contains all the run order types. +- The `get_choices` static method returns the list of choices. +- An example usage is provided to demonstrate how to access the choices. + +dehoni: Please translate the selected code into python and use approbriate packages. + +GitHub Copilot: Here is the translated code into Python: + +```python + + +class WaitForUnits: + MICROAMPS = "Microamp-hours" + MINUTES = "Minutes" + SECONDS = "Seconds" + FRAMES = "Frames" + + choices = [ + MICROAMPS, + MINUTES, + SECONDS, + FRAMES + ] + + @staticmethod + def get_choices(): + return WaitForUnits.choices + +# Example usage +if __name__ == "__main__": + print(WaitForUnits.get_choices()) +``` + +In this Python version: +- The + +WaitForUnits + + class is defined with class attributes for each wait unit type. +- The + +choices + + list contains all the wait unit types. +- The `get_choices` static method returns the list of choices. +- An example usage is provided to demonstrate how to access the choices. + +dehoni: Please translate the selected code into python and use approbriate packages. + +GitHub Copilot: Here is the translated code into Python: + +```python + + +import csv + +class CsvExporter: + @staticmethod + def export(file, runs): + with open(file, mode='w', newline='') as csvfile: + writer = csv.writer(csvfile) + for exp in runs: + row = [ + exp.position, + str(exp.trans), + exp.trans_wait, + str(exp.sans), + exp.sans_wait, + exp.period, + exp.sample, + exp.background, + exp.thickness, + exp.temperature1, + exp.temperature2, + exp.field, + exp.shear_rate1, + exp.shear_rate2, + exp.shear_angle1, + exp.shear_angle2, + exp.pre_command, + exp.post_command, + exp.rb_number + ] + writer.writerow(row) + +# Example usage +class Experiment: + def __init__(self, position="", trans=0, trans_wait="", sans=0, sans_wait="", period="", sample="", background="", thickness="", temperature1="", temperature2="", field="", shear_rate1="", shear_rate2="", shear_angle1="", shear_angle2="", pre_command="", post_command="", rb_number=""): + self.position = position + self.trans = trans + self.trans_wait = trans_wait + self.sans = sans + self.sans_wait = sans_wait + self.period = period + self.sample = sample + self.background = background + self.thickness = thickness + self.temperature1 = temperature1 + self.temperature2 = temperature2 + self.field = field + self.shear_rate1 = shear_rate1 + self.shear_rate2 = shear_rate2 + self.shear_angle1 = shear_angle1 + self.shear_angle2 = shear_angle2 + self.pre_command = pre_command + self.post_command = post_command + self.rb_number = rb_number + +if __name__ == "__main__": + runs = [ + Experiment(position="pos1", trans=10, trans_wait="Minutes", sans=20, sans_wait="Minutes", period="1", sample="Sample1", background="Background1", thickness="1.0", temperature1="25", temperature2="30", field="1.5T", shear_rate1="100", shear_rate2="200", shear_angle1="45", shear_angle2="90", pre_command="Pre Command", post_command="Post Command", rb_number="RB12345") + ] + CsvExporter.export('output.csv', runs) +``` + +In this Python version: +- The + +CsvExporter + + class uses the `csv` module to write data to a CSV file. +- The `export` method writes the properties of each + +Experiment + + object to the CSV file. +- An example + +Experiment + + class and usage are provided to demonstrate how to use the + +CsvExporter + + class. + +dehoni: Please translate the selected code into python and use approbriate packages. + +GitHub Copilot: Here is the translated code into Python using the `pandas` package for handling Excel files: + +```python + + +import pandas as pd +from tkinter import messagebox + +class ExcelIO: + columns = [ + "POSITION", "TRANS", "TRANS_WAIT_FOR", "SANS", "SANS_WAIT_FOR", "PERIOD", "SAMPLE_IDENTIFICATION_STRING", "BACKGROUND", + "THICKNESS", "TEMPERATURE", "TEMPERATURE2", "FIELD", "SHEAR_RATE_1", "SHEAR_RATE_2", "SHEAR_ANGLE_1", "SHEAR_ANGLE_2", + "PRE_COMMAND", "POST_COMMAND", "RB_NUMBER" + ] + + def __init__(self): + self.dataset = None + + def open_excel_workbook(self, filename): + try: + self.dataset = pd.read_excel(filename, sheet_name=None) + except Exception as ex: + messagebox.showerror("SANS Script - Error", str(ex)) + raise IOError("Could not open Excel spreadsheet") + + def extract_experiments(self): + experiments = [] + if self.dataset is not None: + for sheet_name, df in self.dataset.items(): + for _, row in df.iterrows(): + exp = Experiment( + position=row.get("POSITION", ""), + trans=self.cast_to_double(row.get("TRANS", 0)), + trans_wait=row.get("TRANS_WAIT_FOR", ""), + sans=self.cast_to_double(row.get("SANS", 0)), + sans_wait=row.get("SANS_WAIT_FOR", ""), + period=row.get("PERIOD", ""), + sample=row.get("SAMPLE_IDENTIFICATION_STRING", ""), + background=row.get("BACKGROUND", ""), + thickness=row.get("THICKNESS", ""), + temperature1=row.get("TEMPERATURE", ""), + temperature2=row.get("TEMPERATURE2", ""), + field=row.get("FIELD", ""), + shear_rate1=row.get("SHEAR_RATE_1", ""), + shear_rate2=row.get("SHEAR_RATE_2", ""), + shear_angle1=row.get("SHEAR_ANGLE_1", ""), + shear_angle2=row.get("SHEAR_ANGLE_2", ""), + pre_command=row.get("PRE_COMMAND", ""), + post_command=row.get("POST_COMMAND", ""), + rb_number=row.get("RB_NUMBER", "") + ) + experiments.append(exp) + return experiments + + @staticmethod + def cast_to_double(val): + try: + return float(val) + except (ValueError, TypeError): + return 0.0 + + def export_data_to_excel(self, runs, filename): + data = [] + for exp in runs: + data.append([ + exp.position, exp.trans, exp.trans_wait, exp.sans, exp.sans_wait, + exp.period, exp.sample, exp.background, exp.thickness, exp.temperature1, exp.temperature2, exp.field, + exp.shear_rate1, exp.shear_rate2, exp.shear_angle1, exp.shear_angle2, exp.pre_command, + exp.post_command, exp.rb_number + ]) + df = pd.DataFrame(data, columns=self.columns) + df.to_excel(filename, index=False) + +# Example usage +class Experiment: + def __init__(self, position="", trans=0, trans_wait="", sans=0, sans_wait="", period="", sample="", background="", thickness="", temperature1="", temperature2="", field="", shear_rate1="", shear_rate2="", shear_angle1="", shear_angle2="", pre_command="", post_command="", rb_number=""): + self.position = position + self.trans = trans + self.trans_wait = trans_wait + self.sans = sans + self.sans_wait = sans_wait + self.period = period + self.sample = sample + self.background = background + self.thickness = thickness + self.temperature1 = temperature1 + self.temperature2 = temperature2 + self.field = field + self.shear_rate1 = shear_rate1 + self.shear_rate2 = shear_rate2 + self.shear_angle1 = shear_angle1 + self.shear_angle2 = shear_angle2 + self.pre_command = pre_command + self.post_command = post_command + self.rb_number = rb_number + +if __name__ == "__main__": + runs = [ + Experiment(position="pos1", trans=10, trans_wait="Minutes", sans=20, sans_wait="Minutes", period="1", sample="Sample1", background="Background1", thickness="1.0", temperature1="25", temperature2="30", field="1.5T", shear_rate1="100", shear_rate2="200", shear_angle1="45", shear_angle2="90", pre_command="Pre Command", post_command="Post Command", rb_number="RB12345") + ] + excel_io = ExcelIO() + excel_io.export_data_to_excel(runs, 'output.xlsx') + excel_io.open_excel_workbook('output.xlsx') + experiments = excel_io.extract_experiments() + for exp in experiments: + print(exp.__dict__) +``` + +In this Python version: +- The + +ExcelIO + + class uses the `pandas` package to read and write Excel files. +- The `open_excel_workbook` method reads the Excel file into a `pandas` DataFrame. +- The `extract_experiments` method extracts experiment data from the DataFrame. +- The `export_data_to_excel` method writes experiment data to an Excel file. +- An example + +Experiment + + class and usage are provided to demonstrate how to use the + +ExcelIO + + class. + +dehoni: Please translate the selected code into python and use approbriate packages. + +GitHub Copilot: Here is the translated code into Python using the `tkinter` package for creating a similar dialog window: + +```python + + +import tkinter as tk +from tkinter import scrolledtext + +class PreviewDialog(tk.Toplevel): + def __init__(self, parent): + super().__init__(parent) + self.title("PreviewDialog") + self.geometry("600x500") + + self.grid_rowconfigure(0, weight=1) + self.grid_columnconfigure(0, weight=1) + + # Create a frame for the text area with a border + border_frame = tk.Frame(self, borderwidth=1, relief="solid") + border_frame.grid(row=0, column=0, padx=5, pady=5, sticky="nsew") + + # Create a scrollable text area + self.txt_script = scrolledtext.ScrolledText(border_frame, wrap=tk.WORD) + self.txt_script.pack(fill=tk.BOTH, expand=True) + + # Create a frame for the buttons + button_frame = tk.Frame(self) + button_frame.grid(row=1, column=0, pady=5, sticky="e") + + # Create the Print Script button (initially hidden) + self.btn_print = tk.Button(button_frame, text="Print Script", command=self.print_script) + self.btn_print.pack(side=tk.LEFT, padx=5) + self.btn_print.pack_forget() # Hide the button initially + + # Create the Close button + btn_close = tk.Button(button_frame, text="Close", command=self.close) + btn_close.pack(side=tk.LEFT, padx=5) + + def print_script(self): + # Implement the print functionality here + pass + + def close(self): + self.destroy() + +# Example usage +if __name__ == "__main__": + root = tk.Tk() + root.withdraw() # Hide the root window + + dialog = PreviewDialog(root) + dialog.txt_script.insert(tk.END, "This is a preview of the script.") + dialog.mainloop() +``` + +In this Python version: +- The `PreviewDialog` class is defined using the `tkinter` package. +- A `ScrolledText` widget is used to create a scrollable text area. +- A `Frame` widget is used to create a border around the text area. +- A `Frame` widget is used to create a horizontal stack of buttons. +- The `Print Script` button is initially hidden using the `pack_forget` method. +- The `close` method destroys the dialog window. +- An example usage is provided to demonstrate how to create and display the `PreviewDialog`. + +dehoni: Please translate the selected code into python and use approbriate packages. + +GitHub Copilot: Here is the translated code into Python using the `tkinter` package: + +```python + + +import tkinter as tk +from tkinter import scrolledtext + +class PreviewDialog(tk.Toplevel): + def __init__(self, parent): + super().__init__(parent) + self.title("PreviewDialog") + self.geometry("600x500") + + self.grid_rowconfigure(0, weight=1) + self.grid_columnconfigure(0, weight=1) + + # Create a frame for the text area with a border + border_frame = tk.Frame(self, borderwidth=1, relief="solid") + border_frame.grid(row=0, column=0, padx=5, pady=5, sticky="nsew") + + # Create a scrollable text area + self.txt_script = scrolledtext.ScrolledText(border_frame, wrap=tk.WORD) + self.txt_script.pack(fill=tk.BOTH, expand=True) + + # Create a frame for the buttons + button_frame = tk.Frame(self) + button_frame.grid(row=1, column=0, pady=5, sticky="e") + + # Create the Print Script button (initially hidden) + self.btn_print = tk.Button(button_frame, text="Print Script", command=self.print_script) + self.btn_print.pack(side=tk.LEFT, padx=5) + self.btn_print.pack_forget() # Hide the button initially + + # Create the Close button + btn_close = tk.Button(button_frame, text="Close", command=self.close) + btn_close.pack(side=tk.LEFT, padx=5) + + def set_script(self, script): + self.txt_script.delete(1.0, tk.END) + self.txt_script.insert(tk.END, script) + + def print_script(self): + # Implement the print functionality here + pass + + def close(self): + self.destroy() + +# Example usage +if __name__ == "__main__": + root = tk.Tk() + root.withdraw() # Hide the root window + + dialog = PreviewDialog(root) + dialog.set_script("This is a preview of the script.") + dialog.mainloop() +``` + +In this Python version: +- The + +PreviewDialog + + class is defined using the `tkinter` package. +- The `set_script` method sets the text of the `ScrolledText` widget. +- The `print_script` method is a placeholder for the print functionality. +- The `close` method destroys the dialog window. +- An example usage is provided to demonstrate how to create and display the + +PreviewDialog + +. \ No newline at end of file diff --git a/ViewModels/A2Setting.py b/ViewModels/A2Setting.py new file mode 100644 index 0000000..773a782 --- /dev/null +++ b/ViewModels/A2Setting.py @@ -0,0 +1,6 @@ +class A2Setting: + Large = "Large" + Medium = "Medium" + Small = "Small" + XLarge = "XLarge" + Larmor = "Larmor" \ No newline at end of file diff --git a/ViewModels/DataCollection.py b/ViewModels/DataCollection.py new file mode 100644 index 0000000..40ce416 --- /dev/null +++ b/ViewModels/DataCollection.py @@ -0,0 +1,4 @@ +class DataCollection: + @staticmethod + def choices(): + return ["Mode1", "Mode2", "Mode3"] \ No newline at end of file diff --git a/ViewModels/DataGridVM.cs b/ViewModels/DataGridVM.cs new file mode 100644 index 0000000..7dd4481 --- /dev/null +++ b/ViewModels/DataGridVM.cs @@ -0,0 +1,41 @@ +using System.Collections.ObjectModel; +using System.ComponentModel; + +namespace LOQ_Script_Gui +{ + class DataGridVM : INotifyPropertyChanged + { + public event PropertyChangedEventHandler PropertyChanged; + + protected void OnPropertyChanged(string name) + { + PropertyChangedEventHandler handler = PropertyChanged; + if (handler != null) + { + handler(this, new PropertyChangedEventArgs(name)); + } + } + + ObservableCollection runs = new ObservableCollection(); + public ObservableCollection Runs + { + get + { + return runs; + } + set + { + runs = value; + OnPropertyChanged("Runs"); + } + } + + public static ObservableCollection WaitForChoices + { + get + { + return WaitForUnits.Choices; + } + } + } +} diff --git a/ViewModels/DataGridVM.py b/ViewModels/DataGridVM.py new file mode 100644 index 0000000..0b9dfc7 --- /dev/null +++ b/ViewModels/DataGridVM.py @@ -0,0 +1,47 @@ +import tkinter as tk +from tkinter import StringVar +from tkinter import ttk +from Models.WaitForUnits import WaitForUnits +from Models.Experiment import Experiment + + + +class DataGridVM: + def __init__(self): + self._runs = tk.Variable(value=[]) + self.property_changed = [] + + @property + def runs(self): + return self._runs.get() + + @runs.setter + def runs(self, value): + self._runs.set(value) + self.on_property_changed("runs") + + @staticmethod + def wait_for_choices(): + return WaitForUnits.choices() + + def on_property_changed(self, name): + for callback in self.property_changed: + callback(name) + + + + + +if __name__ == "__main__": + root = tk.Tk() + vm = DataGridVM() + + def on_property_changed(name): + print(f"Property {name} changed") + + vm.property_changed.append(on_property_changed) + + vm.runs = [Experiment(), Experiment()] + print(vm.runs) + + root.mainloop() \ No newline at end of file diff --git a/ViewModels/EstimationPanelVM.cs b/ViewModels/EstimationPanelVM.cs new file mode 100644 index 0000000..441ae08 --- /dev/null +++ b/ViewModels/EstimationPanelVM.cs @@ -0,0 +1,24 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Collections.ObjectModel; +using System.ComponentModel; + +namespace LOQ_Script_Gui +{ + class EstimationPanelVM : INotifyPropertyChanged + { + public event PropertyChangedEventHandler PropertyChanged; + + protected void OnPropertyChanged(string name) + { + PropertyChangedEventHandler handler = PropertyChanged; + if (handler != null) + { + handler(this, new PropertyChangedEventArgs(name)); + } + } + } +} diff --git a/ViewModels/EstimationPanelVM.py b/ViewModels/EstimationPanelVM.py new file mode 100644 index 0000000..d9e2c76 --- /dev/null +++ b/ViewModels/EstimationPanelVM.py @@ -0,0 +1,28 @@ +import tkinter as tk + + + +class EstimationPanelVM: + def __init__(self): + self.property_changed = [] + self.count_rate = tk.StringVar() + self.move_time = tk.StringVar() + self.script_time = tk.StringVar() + + def on_property_changed(self, name): + for callback in self.property_changed: + callback(name) + +if __name__ == "__main__": + root = tk.Tk() + vm = EstimationPanelVM() + + def on_property_changed(name): + print(f"Property {name} changed") + + vm.property_changed.append(on_property_changed) + + # Example of triggering property change + vm.on_property_changed("example_property") + + root.mainloop() \ No newline at end of file diff --git a/ViewModels/Geometry.py b/ViewModels/Geometry.py new file mode 100644 index 0000000..bdc6546 --- /dev/null +++ b/ViewModels/Geometry.py @@ -0,0 +1,4 @@ +class Geometry: + @staticmethod + def choices(): + return ["Geometry1", "Geometry2", "Geometry3"] \ No newline at end of file diff --git a/ViewModels/InstrumentVM.cs b/ViewModels/InstrumentVM.cs new file mode 100644 index 0000000..84daf86 --- /dev/null +++ b/ViewModels/InstrumentVM.cs @@ -0,0 +1,179 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Collections.ObjectModel; +using System.ComponentModel; + +namespace LOQ_Script_Gui +{ + class InstrumentVM : INotifyPropertyChanged + { + List loqApertureSizes = new List() + { + A2Setting.Large, A2Setting.Medium, A2Setting.Small + }; + + List sans2dApertureSizes = new List() + { + A2Setting.XLarge, A2Setting.Large, A2Setting.Medium, A2Setting.Small + }; + + List larmorApertureSizes = new List() + { + A2Setting.Larmor + //A2Setting.LarmorLarge, A2Setting.LarmorMedium, A2Setting.LarmorSmall + }; + + List zoomApertureSizes = new List() + { + A2Setting.Large, A2Setting.Medium, A2Setting.Small + }; + + public event PropertyChangedEventHandler PropertyChanged; + + protected void OnPropertyChanged(string name) + { + PropertyChangedEventHandler handler = PropertyChanged; + if (handler != null) + { + handler(this, new PropertyChangedEventArgs(name)); + } + } + + private bool isLarmor = true; + public bool IsLarmor + { + get + { + return isLarmor; + } + set + { + isLarmor = value; + if (value) + { + IsLoq = false; + IsSans2d = false; + IsZoom = false; + InstrumentInfo.ScriptGenerator = new Scripting.GeniePythonScriptGenerator(true,false,false,false); + InstrumentInfo.A2Label = "A1, S1 Setting:"; + InstrumentInfo.A2Choices = new ObservableCollection(larmorApertureSizes); + InstrumentInfo.A2TransEnabled = true; + InstrumentInfo.CollectionModeEnabled = true; + estimation.CountRate = 40; + //settings.A2SettingSans = A2Setting.LarmorMedium; + settings.A2SettingSans = Properties.Settings.Default.A2SettingSans; + //settings.A2SettingTrans = A2Setting.LarmorMedium; + settings.A2SettingTrans = Properties.Settings.Default.A2SettingTrans; + } + + OnPropertyChanged("IsLarmor"); + } + } + + private bool isLoq = false; + public bool IsLoq + { + get + { + return isLoq; + } + set + { + isLoq = value; + if (value) + { + IsLarmor = false; + IsSans2d = false; + IsZoom = false; + InstrumentInfo.ScriptGenerator = new Scripting.GeniePythonScriptGenerator(false,true,false,false); + InstrumentInfo.A2Label = "A2 Setting:"; + InstrumentInfo.A2Choices = new ObservableCollection(loqApertureSizes); + InstrumentInfo.A2TransEnabled = true; + InstrumentInfo.CollectionModeEnabled = true; + estimation.CountRate = 170; + //settings.A2SettingSans = A2Setting.Large; + settings.A2SettingSans = Properties.Settings.Default.A2SettingSans; + //settings.A2SettingTrans = A2Setting.Large; + settings.A2SettingTrans = Properties.Settings.Default.A2SettingTrans; + } + + OnPropertyChanged("IsLoq"); + } + } + + private bool isSans2d = false; + public bool IsSans2d + { + get + { + return isSans2d; + } + set + { + isSans2d = value; + if (value) + { + IsLarmor = false; + IsLoq = false; + IsZoom = false; + //InstrumentInfo.ScriptGenerator = new Scripting.OpenGenieScriptGenerator(true); + InstrumentInfo.ScriptGenerator = new Scripting.GeniePythonScriptGenerator(false, false, false, true); + InstrumentInfo.A2Label = "A6 Setting:"; + InstrumentInfo.A2Choices = new ObservableCollection(sans2dApertureSizes); + InstrumentInfo.A2TransEnabled = true; + InstrumentInfo.CollectionModeEnabled = true; + estimation.CountRate = 40; + //settings.A2SettingSans = A2Setting.Large; + settings.A2SettingSans = Properties.Settings.Default.A2SettingSans; + //settings.A2SettingTrans = A2Setting.Large; + settings.A2SettingTrans = Properties.Settings.Default.A2SettingTrans; + } + + OnPropertyChanged("IsSans2d"); + } + } + + private bool isZoom = false; + public bool IsZoom + { + get + { + return isZoom; + } + set + { + isZoom = value; + if (value) + { + IsLarmor = false; + IsLoq = false; + IsSans2d = false; + InstrumentInfo.ScriptGenerator = new Scripting.GeniePythonScriptGenerator(false,false,true,false); + InstrumentInfo.A2Label = "A6 Setting:"; + InstrumentInfo.A2Choices = new ObservableCollection(zoomApertureSizes); + InstrumentInfo.A2TransEnabled = true; + InstrumentInfo.CollectionModeEnabled = true; + estimation.CountRate = 40; + //settings.A2SettingSans = A2Setting.Large; + settings.A2SettingSans = Properties.Settings.Default.A2SettingSans; + //settings.A2SettingTrans = A2Setting.Large; + settings.A2SettingTrans = Properties.Settings.Default.A2SettingTrans; + } + + OnPropertyChanged("IsZoom"); + } + } + + ExperimentSettings settings; + Estimation estimation; + + public InstrumentVM(ExperimentSettings settings, Estimation estimation) + { + this.settings = settings; + this.estimation = estimation; + } + } +} diff --git a/ViewModels/InstrumentVM.py b/ViewModels/InstrumentVM.py new file mode 100644 index 0000000..49fb38a --- /dev/null +++ b/ViewModels/InstrumentVM.py @@ -0,0 +1,87 @@ +import tkinter as tk +from tkinter import StringVar, BooleanVar +from Models.A2Setting import A2Setting +from Models.InstrumentInfo import InstrumentInfo +from Models.Estimation import Estimation +from Models.ExperimentSettings import ExperimentSettings +from Scripting.GeniePythonScriptGenerator import GeniePythonScriptGenerator +from Properties.Settings import Settings + + +class InstrumentVM: + def __init__(self, settings, estimation): + #def __init__(self, experiment, estimate): + self.settings = settings + self.estimation = estimation + #self.estimate = estimate + #self.experiment = experiment + + + self.loq_aperture_sizes = [A2Setting.LARGE, A2Setting.MEDIUM, A2Setting.SMALL] + self.sans2d_aperture_sizes = [A2Setting.X_LARGE, A2Setting.LARGE, A2Setting.MEDIUM, A2Setting.SMALL] + self.larmor_aperture_sizes = [A2Setting.LARMOR_LARGE,A2Setting.LARMOR_MEDIUM,A2Setting.LARMOR_SMALL] + self.zoom_aperture_sizes = [A2Setting.LARGE, A2Setting.MEDIUM, A2Setting.SMALL] + + self.is_larmor = BooleanVar(value=True) + self.is_loq = BooleanVar(value=False) + self.is_sans2d = BooleanVar(value=False) + self.is_zoom = BooleanVar(value=False) + ''' + self.is_larmor = False + self.is_loq = False + self.is_sans2d = False + self.is_zoom = False + ''' + + self.is_larmor.trace_add("write", self.update_instrument) + self.is_loq.trace_add("write", self.update_instrument) + self.is_sans2d.trace_add("write", self.update_instrument) + self.is_zoom.trace_add("write", self.update_instrument) + + self.update_instrument() + + def update_instrument(self, *args): + if self.is_larmor.get(): + self.set_instrument(True, False, False, False, "A1, S1 Setting:", self.larmor_aperture_sizes, 40) + elif self.is_loq.get(): + self.set_instrument(False, True, False, False, "A2 Setting:", self.loq_aperture_sizes, 170) + elif self.is_sans2d.get(): + self.set_instrument(False, False, True, False, "A6 Setting:", self.sans2d_aperture_sizes, 40) + elif self.is_zoom.get(): + self.set_instrument(False, False, False, True, "A6 Setting:", self.zoom_aperture_sizes, 40) + + def set_instrument(self, larmor, loq, sans2d, zoom, a2_label, a2_choices, count_rate): + self.is_larmor.set(larmor) + self.is_loq.set(loq) + self.is_sans2d.set(sans2d) + self.is_zoom.set(zoom) + + InstrumentInfo.script_generator = GeniePythonScriptGenerator(larmor, loq, zoom, sans2d) + + InstrumentInfo.a2_label = a2_label + InstrumentInfo.a2_choices = a2_choices + InstrumentInfo.a2_trans_enabled = True + InstrumentInfo.collection_mode_enabled = True + self.estimation.count_rate = count_rate + self.settings=Settings() + self.settings.a2_setting_sans = Settings.a2_setting_sans + self.settings.a2_setting_trans = Settings.a2_setting_trans + + + + +if __name__ == "__main__": + root = tk.Tk() + settings = ExperimentSettings() + estimation = Estimation() + vm = InstrumentVM(settings, estimation) + + def on_property_changed(name): + print(f"Property {name} changed") + + vm.is_larmor.trace_add("write", lambda *args: on_property_changed("is_larmor")) + vm.is_loq.trace_add("write", lambda *args: on_property_changed("is_loq")) + vm.is_sans2d.trace_add("write", lambda *args: on_property_changed("is_sans2d")) + vm.is_zoom.trace_add("write", lambda *args: on_property_changed("is_zoom")) + + root.mainloop() \ No newline at end of file diff --git a/ViewModels/RunOrder.py b/ViewModels/RunOrder.py new file mode 100644 index 0000000..bec23ec --- /dev/null +++ b/ViewModels/RunOrder.py @@ -0,0 +1,4 @@ +class RunOrder: + @staticmethod + def choices(): + return ["Choice1", "Choice2", "Choice3"] \ No newline at end of file diff --git a/ViewModels/SettingsPanelVM.cs b/ViewModels/SettingsPanelVM.cs new file mode 100644 index 0000000..d3b6b2d --- /dev/null +++ b/ViewModels/SettingsPanelVM.cs @@ -0,0 +1,87 @@ +using System.Collections.ObjectModel; +using System.ComponentModel; +using System; + +namespace LOQ_Script_Gui +{ + class SettingsPanelVM : INotifyPropertyChanged + { + public event PropertyChangedEventHandler PropertyChanged; + + protected void OnPropertyChanged(string name) + { + PropertyChangedEventHandler handler = PropertyChanged; + if (handler != null) + { + handler(this, new PropertyChangedEventArgs(name)); + } + } + + // Provides the combo box options + public static ObservableCollection OrderChoices + { + get + { + return RunOrder.Choices; + } + } + + // Provides the combo box options + public static ObservableCollection GeometryChoices + { + get + { + return Geometry.Choices; + } + } + + // Provides the combo box options + public static ObservableCollection CollectionModeChoices + { + get + { + return DataCollection.Choices; + } + } + + ExperimentSettings experiment = new ExperimentSettings(); + public ExperimentSettings Experiment + { + get + { + return experiment; + } + set + { + experiment = value; + OnPropertyChanged("Experiment"); + } + } + + InstrumentInfo instInfo; + public InstrumentInfo InstInfo + { + get + { + return instInfo; + } + set + { + instInfo = value; + OnPropertyChanged("InstInfo"); + } + } + + public SettingsPanelVM() + { + // Need a default constructor to keep the databinding happy! + // The other constructor should be called ASAP + instInfo = new InstrumentInfo(); + } + + public SettingsPanelVM(InstrumentInfo instInfo) + { + this.instInfo = instInfo; + } + } +} diff --git a/ViewModels/SettingsPanelVM.py b/ViewModels/SettingsPanelVM.py new file mode 100644 index 0000000..a5a5a2a --- /dev/null +++ b/ViewModels/SettingsPanelVM.py @@ -0,0 +1,75 @@ +import tkinter as tk +from tkinter import StringVar +from Models.Estimation import Estimation +from Models.Experiment import Experiment +from Models.ExperimentSettings import ExperimentSettings +from Models.InstrumentInfo import InstrumentInfo +from Models.DataCollection import DataCollection +from Models.RunOrder import RunOrder +from Models.Geometry import Geometry + +class SettingsPanelVM: + def __init__(self, inst_info=None): + self.property_changed = [] + self.order_choices = ["Choice1", "Choice2", "Choice3"] + self.geometry_choices = ["Geometry1", "Geometry2", "Geometry3"] + + + self._experiment = ExperimentSettings() + self._inst_info = inst_info if inst_info is not None else InstrumentInfo() + + def on_property_changed(self, name): + for callback in self.property_changed: + callback(name) + + @property + def experiment(self): + return self._experiment + + @experiment.setter + def experiment(self, value): + self._experiment = value + self.on_property_changed("experiment") + + @property + def inst_info(self): + return self._inst_info + + @inst_info.setter + def inst_info(self, value): + self._inst_info = value + self.on_property_changed("inst_info") + + @staticmethod + def order_choices(): + return RunOrder.choices() + + @staticmethod + def geometry_choices(): + return Geometry.choices() + + @staticmethod + def collection_mode_choices(): + return DataCollection.choices() + + + + + + + + + +if __name__ == "__main__": + root = tk.Tk() + vm = SettingsPanelVM() + + def on_property_changed(name): + print(f"Property {name} changed") + + vm.property_changed.append(on_property_changed) + + vm.experiment = ExperimentSettings() + vm.inst_info = InstrumentInfo() + + root.mainloop() \ No newline at end of file diff --git a/ViewModels/WaitForUnits.py b/ViewModels/WaitForUnits.py new file mode 100644 index 0000000..69c86ba --- /dev/null +++ b/ViewModels/WaitForUnits.py @@ -0,0 +1,4 @@ +class WaitForUnits: + @staticmethod + def choices(): + return ["Choice1", "Choice2", "Choice3"] \ No newline at end of file diff --git a/app_config.ini b/app_config.ini new file mode 100644 index 0000000..1e228bb --- /dev/null +++ b/app_config.ini @@ -0,0 +1,13 @@ +[DEFAULT] +LastInstrument = 'LARMOR' +SampleHeight = '11' +SampleWidth = '11' +Geometry = 'Flat Plate' +RunOrder = 'Alternate - TRANS first' +NumTrans = '1' +NumSans = '1' +LoopPerRun = 'False' +A2SettingSans = 'Large = 25, 16 mm' +A2SettingTrans = 'Large = 25, 16 mm' +CollectionMode = 'Events' + From 21b2ed6ee669f73ecee400add2e183505b689277 Mon Sep 17 00:00:00 2001 From: Dirk Honecker Date: Sun, 15 Dec 2024 15:15:43 +0000 Subject: [PATCH 47/88] radiobutton for instrument selection --- App.py | 4 +++- Dialogs/PreviewDialog.py | 6 +++--- EstimationPanel.py | 4 +++- MainWindow.py | 43 ++++++++++++++++++---------------------- Properties/Settings.ini | 4 ++-- SettingsPanel.py | 31 ++++++++++++++++------------- 6 files changed, 47 insertions(+), 45 deletions(-) diff --git a/App.py b/App.py index 86d8cb9..a2ba1c5 100644 --- a/App.py +++ b/App.py @@ -5,7 +5,9 @@ class App: def __init__(self): self.root = tk.Tk() self.main_window = MainWindow(self.root) - self.main_window.pack(fill=tk.BOTH, expand=True) + self.main_window.grid(sticky="nsew") + self.root.grid_rowconfigure(0, weight=1) + self.root.grid_columnconfigure(0, weight=1) def run(self): self.root.mainloop() diff --git a/Dialogs/PreviewDialog.py b/Dialogs/PreviewDialog.py index 167bcb8..4138fa3 100644 --- a/Dialogs/PreviewDialog.py +++ b/Dialogs/PreviewDialog.py @@ -18,7 +18,7 @@ def __init__(self, parent): # Create a scrollable text area self.txt_script = scrolledtext.ScrolledText(border_frame, wrap=tk.WORD) - self.txt_script.pack(fill=tk.BOTH, expand=True) + self.txt_script.grid(row=0, column=0, sticky="nsew") self.script_text.insert(tk.END, self.txt_script) self.script_text.config(state=tk.DISABLED) @@ -28,12 +28,12 @@ def __init__(self, parent): # Create the Print Script button (initially hidden) self.btn_print = tk.Button(button_frame, text="Print Script", command=self.print_script) - self.btn_print.pack(side=tk.LEFT, padx=5) + self.btn_print.grid(row=0, column=0, padx=5) self.btn_print.pack_forget() # Hide the button initially # Create the Close button btn_close = tk.Button(button_frame, text="Close", command=self.close) - btn_close.pack(side=tk.LEFT, padx=5) + btn_close.grid(row=0, column=1, padx=5) def set_script(self, script): self.txt_script.delete(1.0, tk.END) diff --git a/EstimationPanel.py b/EstimationPanel.py index 5f81c01..6372d99 100644 --- a/EstimationPanel.py +++ b/EstimationPanel.py @@ -58,5 +58,7 @@ def btn_calculate_click(self): root = tk.Tk() estimate = EstimationPanelVM() panel = EstimationPanel(root, estimate) - panel.pack(fill=tk.BOTH, expand=True) + panel.grid(sticky="nsew") + root.grid_rowconfigure(0, weight=1) + root.grid_columnconfigure(0, weight=1) root.mainloop() \ No newline at end of file diff --git a/MainWindow.py b/MainWindow.py index 9f064e1..435c73a 100644 --- a/MainWindow.py +++ b/MainWindow.py @@ -41,11 +41,18 @@ def create_widgets(self): - #self.pnl_settings.pack(fill=tk.X, expand=True, padx=5, pady=5) - # Data Grid - self.data_grid = ttk.Treeview(self, columns=("POSITION", "TRANS", "TRANS__WAIT", "SANS", "SANS__WAIT", "PERIOD", "SAMPLE__IDENTIFICATION__STRING", "BACKGROUND", "THICKNESS", "TEMPERATURE", "TEMPERATURE2", "FIELD", "SHEAR__RATE__1", "SHEAR__RATE__2", "SHEAR__ANGLE__1", "SHEAR__ANGLE__2", "PRE__COMMAND", "POST__COMMAND", "RB__NUMBER"), show="headings") - #self.pnl_settings.pack(fill=tk.X, expand=True, padx=5, pady=5) - self.data_grid.grid(row=0, column=1, rowspan=4, sticky="nsew", padx=5, pady=5) + + # Data Grid with horizontal scrollbar + data_frame = ttk.Frame(self) + data_frame.grid(row=0, column=1, rowspan=4, sticky="nsew", padx=5, pady=5) + + self.data_grid = ttk.Treeview(data_frame, columns=("POSITION", "TRANS", "TRANS__WAIT", "SANS", "SANS__WAIT", "PERIOD", "SAMPLE__IDENTIFICATION__STRING", "BACKGROUND", "THICKNESS", "TEMPERATURE", "TEMPERATURE2", "FIELD", "SHEAR__RATE__1", "SHEAR__RATE__2", "SHEAR__ANGLE__1", "SHEAR__ANGLE__2", "PRE__COMMAND", "POST__COMMAND", "RB__NUMBER"), show="headings") + + self.data_grid.grid(row=0, column=0, sticky="nsew") + + scrollbar = ttk.Scrollbar(data_frame, orient=tk.HORIZONTAL, command=self.data_grid.xview) + scrollbar.grid(row=1, column=0, sticky="ns") + self.data_grid.configure(xscrollcommand=scrollbar.set) for col in self.data_grid["columns"]: self.data_grid.heading(col, text=col.replace("__", " ")) # Settings Panel @@ -81,14 +88,11 @@ def create_widgets(self): instrument_menu = tk.Menu(self.mnu_main, tearoff=0) self.mnu_main.add_cascade(label="Instrument", menu=instrument_menu) - self.is_larmor = tk.BooleanVar() - self.is_loq = tk.BooleanVar() - self.is_sans2d = tk.BooleanVar() - self.is_zoom = tk.BooleanVar() - instrument_menu.add_checkbutton(label="LARMOR", variable=self.is_larmor) - instrument_menu.add_checkbutton(label="LOQ", variable=self.is_loq) - instrument_menu.add_checkbutton(label="SANS2D", variable=self.is_sans2d) - instrument_menu.add_checkbutton(label="ZOOM", variable=self.is_zoom) + self.instrument_var = tk.StringVar(value="LARMOR") + instrument_menu.add_radiobutton(label="LARMOR", variable=self.instrument_var, value="LARMOR") + instrument_menu.add_radiobutton(label="LOQ", variable=self.instrument_var, value="LOQ") + instrument_menu.add_radiobutton(label="SANS2D", variable=self.instrument_var, value="SANS2D") + instrument_menu.add_radiobutton(label="ZOOM", variable=self.instrument_var, value="ZOOM") @@ -98,19 +102,10 @@ def create_widgets(self): def load_last_instrument(self): inst = "LARMOR" # Replace with actual settings retrieval - if inst == "LARMOR": - self.instrument.is_larmor = True - elif inst == "LOQ": - self.instrument.is_loq = True - elif inst == "SANS2D": - self.instrument.is_sans2d = True - elif inst == "ZOOM": - self.instrument.is_zoom = True - else: - self.instrument.is_larmor = True + self.instrument_var.set(inst) def pnl_settings_on_write_clicked(self): - filetypes = [("genie_python Files", "*.py")] if self.instrument.is_larmor or self.instrument.is_loq or self.instrument.is_zoom or self.instrument.is_sans2d else [("Open Genie Files", "*.gcl")] + filetypes = [("genie_python Files", "*.py")] if self.instrument_var.get() in ["LARMOR", "LOQ", "ZOOM", "SANS2D"] else [("Open Genie Files", "*.gcl")] filepath = filedialog.asksaveasfilename(defaultextension=filetypes[0][1], filetypes=filetypes) if filepath: scriptname = filepath.split('/')[-1].replace(" ", "_").rsplit('.', 1)[0] diff --git a/Properties/Settings.ini b/Properties/Settings.ini index 76b9093..8e8260a 100644 --- a/Properties/Settings.ini +++ b/Properties/Settings.ini @@ -7,7 +7,7 @@ runorder = 'Alternate - TRANS first' numtrans = '1' numsans = '1' loopperrun = 'False' -a2settingsans = -a2settingtrans = +a2settingsans = +a2settingtrans = collectionmode = 'Events' diff --git a/SettingsPanel.py b/SettingsPanel.py index 2938fed..76b1f91 100644 --- a/SettingsPanel.py +++ b/SettingsPanel.py @@ -21,10 +21,11 @@ def create_widgets(self): label = ttk.Label(self, text="Settings", font=("Helvetica", 16, "bold")) - label.pack(pady=10) + label.grid(row=0, column=0, columnspan=2, pady=10) grid_frame = ttk.Frame(self) - grid_frame.pack(padx=10, pady=10) + grid_frame.grid(row=1, column=0, columnspan=2, padx=10, pady=10, sticky="nsew") + # Order ttk.Label(grid_frame, text="Order:").grid(row=0, column=0, sticky=tk.E) @@ -36,16 +37,16 @@ def create_widgets(self): sans_frame = ttk.Frame(grid_frame) sans_frame.grid(row=1, column=1, columnspan=2) self.txt_times_sans = ttk.Entry(sans_frame, width=5) - self.txt_times_sans.pack(side=tk.LEFT, padx=5, pady=5) - ttk.Label(sans_frame, text="time(s)").pack(side=tk.LEFT) + self.txt_times_sans.grid(row=0, column=0, padx=5, pady=5) + ttk.Label(sans_frame, text="time(s)").grid(row=0, column=1) # Do TRANS ttk.Label(grid_frame, text="Do TRANS:").grid(row=2, column=0, sticky=tk.E) trans_frame = ttk.Frame(grid_frame) trans_frame.grid(row=2, column=1, columnspan=2) self.txt_times_trans = ttk.Entry(trans_frame, width=5) - self.txt_times_trans.pack(side=tk.LEFT, padx=5, pady=5) - ttk.Label(trans_frame, text="time(s)").pack(side=tk.LEFT) + self.txt_times_trans.grid(row=0, column=0, padx=5, pady=5) + ttk.Label(trans_frame, text="time(s)").grid(row=0, column=1) # Loop over each run self.chk_loop_run = ttk.Checkbutton(grid_frame, text="Loop over each run?") @@ -61,24 +62,24 @@ def create_widgets(self): sample_height_frame = ttk.Frame(grid_frame) sample_height_frame.grid(row=7, column=1, columnspan=2) self.txt_sample_height = ttk.Entry(sample_height_frame, width=5) - self.txt_sample_height.pack(side=tk.LEFT, padx=5, pady=5) - ttk.Label(sample_height_frame, text="mm").pack(side=tk.LEFT) + self.txt_sample_height.grid(row=0, column=0, padx=5, pady=5) + ttk.Label(sample_height_frame, text="mm").grid(row=0, column=1) # Sample Width ttk.Label(grid_frame, text="Sample Width:").grid(row=8, column=0, sticky=tk.E) sample_width_frame = ttk.Frame(grid_frame) sample_width_frame.grid(row=8, column=1, columnspan=2) self.txt_sample_width = ttk.Entry(sample_width_frame, width=5) - self.txt_sample_width.pack(side=tk.LEFT, padx=5, pady=5) - ttk.Label(sample_width_frame, text="mm").pack(side=tk.LEFT) + self.txt_sample_width.grid(row=0, column=0, padx=5, pady=5) + ttk.Label(sample_width_frame, text="mm").grid(row=0, column=1) # Buttons button_frame = ttk.Frame(self) - button_frame.pack(pady=10) + button_frame.grid(row=2, column=0, columnspan=2, pady=10) self.btn_preview_script = ttk.Button(button_frame, text="Preview Script", command=self.btn_preview_script_click) - self.btn_preview_script.pack(side=tk.LEFT, padx=5) + self.btn_preview_script.grid(row=0, column=0, padx=5) self.btn_write_script = ttk.Button(button_frame, text="Write Script", command=self.btn_write_script_click) - self.btn_write_script.pack(side=tk.LEFT, padx=5) + self.btn_write_script.grid(row=0, column=1, padx=5) def btn_preview_script_click(self): if self.on_preview_clicked: @@ -94,5 +95,7 @@ def btn_write_script_click(self): if __name__ == "__main__": root = tk.Tk() panel = SettingsPanel(root) - panel.pack() + panel.grid(sticky="nsew") + root.grid_rowconfigure(0, weight=1) + root.grid_columnconfigure(0, weight=1) root.mainloop() \ No newline at end of file From d404eea8417bbda8c3d8f254b426630ffdf5a178 Mon Sep 17 00:00:00 2001 From: Dirk Honecker Date: Sun, 22 Dec 2024 06:10:28 +0000 Subject: [PATCH 48/88] Setting up repository --- App.py | 4 +- MainWindow.py | 58 ++-- Models/InstrumentInfo.py | 4 +- Properties/Settings.ini | 4 +- SANS_Script_GUI_v4.csproj.py | 3 +- Scripting/IScriptGenerator.py | 10 - Scripting/OpenGenieScriptGenerator.py | 309 ------------------ ...nScriptGenerator.py => ScriptGenerator.py} | 8 +- SettingsPanel.py | 1 + ViewModels/DataCollection.py | 4 - ViewModels/Geometry.py | 4 - ViewModels/InstrumentVM.py | 4 +- ViewModels/RunOrder.py | 4 - ViewModels/SettingsPanelVM.py | 14 +- 14 files changed, 57 insertions(+), 374 deletions(-) delete mode 100644 Scripting/IScriptGenerator.py delete mode 100644 Scripting/OpenGenieScriptGenerator.py rename Scripting/{GeniePythonScriptGenerator.py => ScriptGenerator.py} (97%) delete mode 100644 ViewModels/DataCollection.py delete mode 100644 ViewModels/Geometry.py delete mode 100644 ViewModels/RunOrder.py diff --git a/App.py b/App.py index a2ba1c5..27e06fe 100644 --- a/App.py +++ b/App.py @@ -6,8 +6,8 @@ def __init__(self): self.root = tk.Tk() self.main_window = MainWindow(self.root) self.main_window.grid(sticky="nsew") - self.root.grid_rowconfigure(0, weight=1) - self.root.grid_columnconfigure(0, weight=1) + self.root.grid_rowconfigure(1, weight=1) + self.root.grid_columnconfigure(1, weight=1) def run(self): self.root.mainloop() diff --git a/MainWindow.py b/MainWindow.py index 435c73a..bde4c06 100644 --- a/MainWindow.py +++ b/MainWindow.py @@ -3,7 +3,6 @@ from tkinter.scrolledtext import ScrolledText import pandas as pd from Models.DataCollection import DataCollection -from Models.RunOrder import RunOrder from ViewModels.DataGridVM import DataGridVM from ViewModels.SettingsPanelVM import SettingsPanelVM from ViewModels.InstrumentVM import InstrumentVM @@ -35,26 +34,44 @@ def __init__(self,parent): def create_widgets(self): #self.data_grid = ttk.Treeview(self) self.grid_columnconfigure(1, weight=1) - self.grid_rowconfigure(0, weight=1) + self.grid_rowconfigure(1, weight=1) - # Data Grid with horizontal scrollbar - data_frame = ttk.Frame(self) - data_frame.grid(row=0, column=1, rowspan=4, sticky="nsew", padx=5, pady=5) - - self.data_grid = ttk.Treeview(data_frame, columns=("POSITION", "TRANS", "TRANS__WAIT", "SANS", "SANS__WAIT", "PERIOD", "SAMPLE__IDENTIFICATION__STRING", "BACKGROUND", "THICKNESS", "TEMPERATURE", "TEMPERATURE2", "FIELD", "SHEAR__RATE__1", "SHEAR__RATE__2", "SHEAR__ANGLE__1", "SHEAR__ANGLE__2", "PRE__COMMAND", "POST__COMMAND", "RB__NUMBER"), show="headings") + # Data Grid with horizontal and vertical scrollbars - self.data_grid.grid(row=0, column=0, sticky="nsew") + + ''' + self.canvas = tk.Canvas(self, borderwidth=0, background="#ffffff") + self.data_frame = tk.Frame(self.canvas, background="#f0ffff") + #data_frame = ttk.Frame(self) + self.scrolly = tk.Scrollbar(self, orient="vertical", command=self.canvas.yview) + self.scrollx = tk.Scrollbar(self, orient="horizontal", command=self.canvas.xview) + self.canvas.configure(xscrollcommand=self.scrollx.set) + self.canvas.configure(yscrollcommand=self.scrolly.set) + self.canvas.create_window((4,4), window=self.data_frame, anchor="nw", tags="self.frame") + + self.data_frame.bind("", self.onFrameConfigure) + ''' - scrollbar = ttk.Scrollbar(data_frame, orient=tk.HORIZONTAL, command=self.data_grid.xview) - scrollbar.grid(row=1, column=0, sticky="ns") - self.data_grid.configure(xscrollcommand=scrollbar.set) + self.data_frame = ttk.Frame(self) + self.data_frame.grid(row=0, column=1, rowspan=4, sticky="nsew", padx=5, pady=5) + self.data_grid = ttk.Treeview(self.data_frame, columns=("POSITION", "TRANS", "TRANS_WAIT", "SANS", "SANS_WAIT", "PERIOD", "SAMPLE_IDENTIFICATION_STRING", "BACKGROUND", "THICKNESS", "TEMPERATURE", "TEMPERATURE2", "FIELD", "SHEAR_RATE_1", "SHEAR_RATE_2", "SHEAR_ANGLE_1", "SHEAR_ANGLE_2", "PRE_COMMAND", "POST_COMMAND", "RB_NUMBER"), show="headings") + self.data_grid.grid(row=0, column=0, sticky="nsew") + scrollbar_y = ttk.Scrollbar(self.data_frame, orient=tk.VERTICAL, command=self.data_grid.yview) + scrollbar_y.grid(row=0, column=1, sticky="ns") + self.data_grid.configure(yscrollcommand=scrollbar_y.set) + + scrollbar_x = ttk.Scrollbar(self.data_frame, orient=tk.HORIZONTAL, command=self.data_grid.xview) + scrollbar_x.grid(row=1, column=0, sticky="ew") + self.data_grid.configure(xscrollcommand=scrollbar_x.set) + + for col in self.data_grid["columns"]: - self.data_grid.heading(col, text=col.replace("__", " ")) + self.data_grid.heading(col, text=col.replace("_", " ")) # Settings Panel self.pnl_settings = SettingsPanel(self, self.settings) self.pnl_settings.grid(row=0, column=0, sticky="ew") @@ -72,10 +89,7 @@ def create_widgets(self): self.btn_open_excel = ttk.Button(self, text="Open Excel", command=self.btn_open_excel_click) self.btn_open_excel.grid(row=3, column=0, padx=5, pady=5, sticky="w") - - - - + self.mnu_main = tk.Menu(self.parent) self.parent.config(menu=self.mnu_main) @@ -93,13 +107,10 @@ def create_widgets(self): instrument_menu.add_radiobutton(label="LOQ", variable=self.instrument_var, value="LOQ") instrument_menu.add_radiobutton(label="SANS2D", variable=self.instrument_var, value="SANS2D") instrument_menu.add_radiobutton(label="ZOOM", variable=self.instrument_var, value="ZOOM") - - - - - - - + ''' + def onFrameConfigure(self, event): + self.canvas.configure(scrollregion=self.canvas.bbox("all")) + ''' def load_last_instrument(self): inst = "LARMOR" # Replace with actual settings retrieval self.instrument_var.set(inst) @@ -115,6 +126,7 @@ def pnl_settings_on_write_clicked(self): def pnl_settings_on_preview_clicked(self): script = self.inst_info.script_generator.generate_script(self.data.runs, self.settings.experiment, "my_script") + print(script) preview_dialog = PreviewDialog(self, script) preview_dialog.show() diff --git a/Models/InstrumentInfo.py b/Models/InstrumentInfo.py index 72598aa..a0928d9 100644 --- a/Models/InstrumentInfo.py +++ b/Models/InstrumentInfo.py @@ -75,7 +75,7 @@ def script_generator(self): @script_generator.setter def script_generator(self, value): self._script_generator = value - +''' class ScriptGenerator: @staticmethod def generate_script(runs, experiment, scriptname): @@ -84,7 +84,7 @@ def generate_script(runs, experiment, scriptname): @staticmethod def estimate_run_time(runs, experiment, estimate): return len(runs) * 10 # Dummy implementation - +''' # Example usage if __name__ == "__main__": def on_property_changed(name): diff --git a/Properties/Settings.ini b/Properties/Settings.ini index 8e8260a..37ce0f0 100644 --- a/Properties/Settings.ini +++ b/Properties/Settings.ini @@ -7,7 +7,7 @@ runorder = 'Alternate - TRANS first' numtrans = '1' numsans = '1' loopperrun = 'False' -a2settingsans = -a2settingtrans = +a2settingsans = +a2settingtrans = collectionmode = 'Events' diff --git a/SANS_Script_GUI_v4.csproj.py b/SANS_Script_GUI_v4.csproj.py index 10d6092..f312438 100644 --- a/SANS_Script_GUI_v4.csproj.py +++ b/SANS_Script_GUI_v4.csproj.py @@ -89,12 +89,11 @@ "Models\\A2Setting.py", "Models\\Estimation.py", "Models\\Geometry.py", - "Models\\RunOrder.py", "EstimationPanel.xaml.py", "Models\\Experiment.py", "Models\\ExperimentSettings.py", "Models\\WaitForUnits.py", - "Scripting\\GeniePythonScriptGenerator.py", + "Scripting\\ScriptGenerator.py", "Scripting\\IScriptGenerator.py", "Scripting\\OpenGenieScriptGenerator.py", "SettingsPanel.xaml.py", diff --git a/Scripting/IScriptGenerator.py b/Scripting/IScriptGenerator.py deleted file mode 100644 index 4b8aca1..0000000 --- a/Scripting/IScriptGenerator.py +++ /dev/null @@ -1,10 +0,0 @@ -from abc import ABC, abstractmethod - -class IScriptGenerator(ABC): - @abstractmethod - def generate_script(self, runs, settings, scriptname): - pass - - @abstractmethod - def estimate_run_time(self, runs, settings, estimation): - pass diff --git a/Scripting/OpenGenieScriptGenerator.py b/Scripting/OpenGenieScriptGenerator.py deleted file mode 100644 index f8329ef..0000000 --- a/Scripting/OpenGenieScriptGenerator.py +++ /dev/null @@ -1,309 +0,0 @@ -from datetime import datetime, timedelta -from Models.Estimation import Estimation -from Models.Experiment import Experiment -from Models.ExperimentSettings import ExperimentSettings - -class OpenGenieScriptGenerator: - def __init__(self, is_sans2d): - self.is_sans2d = is_sans2d - - def generate_script(self, runs, settings, scriptname): - script = f"# Script created by LOQ Script at {datetime.now()}\n" - script += "# Script MUST be loaded in to Open GENIE using the loadscript command.\n\n" - - if self.is_sans2d: - script += "QUALIFIERS /test\n" - - script += f"LOCAL transCount = 0 sansCount = 0 numTRANS={settings.num_trans} numSANS={settings.num_sans}\n\n" - script += "SETSCRIPTNAME THIS_PROCEDURE()\n\n" - script += f'Sample_par width={settings.sample_width} height={settings.sample_height} geometry="{settings.sample_geometry}"\n\n' - - if self.is_sans2d: - position_list = ",".join([exp.position for exp in runs if exp.position]) - if position_list: - script += f'IF NOT CHECK_MOVE_POS("{position_list}")\n' - script += ' PRINTEN "Invalid positions - script aborted"\n' - script += ' RETURN\n' - script += 'ENDIF\n\n' - script += "IF test\n" - script += " RETURN\n" - script += "ENDIF\n\n" - - if settings.order == "AllTrans": - if settings.loop_per_run: - script += self.do_all_trans_loop_each(runs, settings, True, True) - script += self.do_all_sans_loop_each(runs, settings, True, True) - else: - script += "LOOP\n\n" - script += self.do_all_trans(runs, settings, True, True) - script += self.do_all_sans(runs, settings, True, True) - script += "EXITIF ((transCount >= numTRANS) AND (sansCount >= numSANS))\n\n" - script += "ENDLOOP\n\n" - elif settings.order == "TransFirst": - if settings.loop_per_run: - for exp in runs: - script += self.do_single_trans_loop_each(exp, settings, True, False) - script += self.do_single_sans_loop_each(exp, settings, False, True) - else: - script += "LOOP\n\n" - for exp in runs: - script += self.do_single_trans(exp, settings, True, False) - script += self.do_single_sans(exp, settings, False, True) - script += "transCount = transCount + 1\n" - script += "sansCount = sansCount + 1\n\n" - script += "EXITIF ((transCount >= numTRANS) AND (sansCount >= numSANS))\n\n" - script += "ENDLOOP\n\n" - elif settings.order == "AllSans": - if settings.loop_per_run: - script += self.do_all_sans_loop_each(runs, settings, True, True) - script += self.do_all_trans_loop_each(runs, settings, True, True) - else: - script += "LOOP\n\n" - script += self.do_all_sans(runs, settings, True, True) - script += self.do_all_trans(runs, settings, True, True) - script += "EXITIF ((transCount >= numTRANS) AND (sansCount >= numSANS))\n\n" - script += "ENDLOOP\n\n" - elif settings.order == "SansFirst": - if settings.loop_per_run: - for exp in runs: - script += self.do_single_sans_loop_each(exp, settings, True, False) - script += self.do_single_trans_loop_each(exp, settings, False, True) - else: - script += "LOOP\n\n" - for exp in runs: - script += self.do_single_sans(exp, settings, True, False) - script += self.do_single_trans(exp, settings, False, True) - script += "transCount = transCount + 1\n" - script += "sansCount = sansCount + 1\n\n" - script += "EXITIF ((transCount >= numTRANS) AND (sansCount >= numSANS))\n\n" - script += "ENDLOOP\n\n" - - script += 'PRINTN "SCRIPT COMPLETED "\n\n' - return script - - def do_single_sans(self, exp, settings, do_pre_com, do_post_com): - script = "" - if settings.num_sans > 0 and exp.sans > 0: - script += "IF (sansCount < numSANS)\n" - script += self.set_sans_aperture(settings.a2_setting_sans) - script += self.do_single_run(False, exp, False, do_pre_com, do_post_com, settings) - script += "\nENDIF\n\n" - return script - - def do_single_trans(self, exp, settings, do_pre_com, do_post_com): - script = "" - if settings.num_trans > 0 and exp.trans > 0: - script += "IF (transCount < numTRANS)\n" - script += self.set_trans_aperture(settings.a2_setting_trans) - script += self.do_single_run(True, exp, False, do_pre_com, do_post_com, settings) - script += "\nENDIF\n\n" - return script - - def do_single_sans_loop_each(self, exp, settings, do_pre_com, do_post_com): - script = "" - if settings.num_sans > 0 and exp.sans > 0: - script += self.set_sans_aperture(settings.a2_setting_sans) - script += self.do_single_run(False, exp, True, do_pre_com, do_post_com, settings) + "\n" - return script - - def do_single_trans_loop_each(self, exp, settings, do_pre_com, do_post_com): - script = "" - if settings.num_trans > 0 and exp.trans > 0: - script += self.set_trans_aperture(settings.a2_setting_trans) - script += self.do_single_run(True, exp, True, do_pre_com, do_post_com, settings) + "\n" - return script - - def do_all_sans(self, runs, settings, do_pre_com, do_post_com): - script = "" - if settings.num_sans > 0: - script += self.set_sans_aperture(settings.a2_setting_sans) - script += "IF sansCount < numSANS\n\n" - for exp in runs: - script += self.do_single_run(False, exp, False, do_pre_com, do_post_com, settings) + "\n" - script += "sansCount = sansCount + 1\n\n" - script += "ENDIF\n\n" - return script - - def do_all_trans(self, runs, settings, do_pre_com, do_post_com): - script = "" - if settings.num_trans > 0: - script += self.set_trans_aperture(settings.a2_setting_trans) - script += "IF transCount < numTRANS\n\n" - for exp in runs: - script += self.do_single_run(True, exp, False, do_pre_com, do_post_com, settings) + "\n" - script += "transCount = transCount + 1\n\n" - script += "ENDIF\n\n" - return script - - def do_all_sans_loop_each(self, runs, settings, do_pre_com, do_post_com): - script = "" - if settings.num_sans > 0: - script += self.set_sans_aperture(settings.a2_setting_sans) - for exp in runs: - script += self.do_single_run(False, exp, True, do_pre_com, do_post_com, settings) + "\n" - return script - - def do_all_trans_loop_each(self, runs, settings, do_pre_com, do_post_com): - script = "" - if settings.num_trans > 0: - script += self.set_trans_aperture(settings.a2_setting_trans) - for exp in runs: - script += self.do_single_run(True, exp, True, do_pre_com, do_post_com, settings) + "\n" - return script - - def set_sans_aperture(self, a2_size_sans): - script = "" - if a2_size_sans == "Large": - script += "DO_SANS /LARGE\n\n" - elif a2_size_sans == "Medium": - script += "DO_SANS /MEDIUM\n\n" - elif a2_size_sans == "Small": - script += "DO_SANS /SMALL\n\n" - elif a2_size_sans == "Trans": - script += "DO_SANS /TRANS\n\n" - return script - - def set_trans_aperture(self, a2_size_trans): - script = "" - if not a2_size_trans: - script += "DO_TRANS\n\n" - else: - if a2_size_trans == "Large": - script += "DO_TRANS /LARGE\n\n" - elif a2_size_trans == "Medium": - script += "DO_TRANS /MEDIUM\n\n" - elif a2_size_trans == "Small": - script += "DO_TRANS /SMALL\n\n" - elif a2_size_trans == "Trans": - script += "DO_TRANS /TRANS\n\n" - return script - - def do_single_run(self, is_trans, exp, is_loop, do_pre_com, do_post_com, settings): - script = "" - if exp.trans > 0 or exp.sans > 0: - if exp.rb_number: - script += f"change rbno={exp.rb_number}\n" - if exp.position: - if do_pre_com: - script += self.do_sample_environment(exp) - if is_loop: - if is_trans: - script += "LOOP transCount FROM 1 TO numTRANS\n" - else: - script += "LOOP sansCount FROM 1 TO numSANS\n" - script += self.create_move_command(is_trans, exp, settings) - script += "ENDLOOP\n" - else: - script += self.create_move_command(is_trans, exp, settings) - if do_post_com and exp.post_command: - script += exp.post_command + "\n" - return script - - def do_sample_environment(self, exp): - script = "" - if exp.temperature1: - script += f"CSET TEMP={exp.temperature1}\n" - if exp.temperature2: - script += f"CSET TEMP2={exp.temperature2}\n" - if exp.pre_command: - script += exp.pre_command + "\n" - if exp.field: - script += f"CSET FIELD={exp.field}\n" - if exp.shear_rate1: - script += f"CSET SHEAR_RATE_1={exp.shear_rate1}\n" - if exp.shear_rate2: - script += f"CSET SHEAR_RATE_2={exp.shear_rate2}\n" - if exp.shear_angle1: - script += f"CSET SHEAR_ANGLE_1={exp.shear_angle1}\n" - if exp.shear_angle2: - script += f"CSET SHEAR_ANGLE_2={exp.shear_angle2}\n" - return script - - def create_move_command(self, is_trans, exp, settings): - script = f'MOVE pos="{exp.position}"' - fulltitle = f"{{{exp.sample}}}" - if exp.background: - fulltitle += f"_{{{exp.background}}}" - if exp.thickness: - script += f" thick={exp.thickness}" - else: - script += " thick=1" - if exp.period: - script += f" period={exp.period}" - if is_trans: - script += self.write_wait_for(exp.trans_wait, exp.trans) - if fulltitle: - script += f' title="{fulltitle}_TRANS"' - else: - script += self.write_wait_for(exp.sans_wait, exp.sans) - if fulltitle: - if settings.collection_mode in ["TRANSSANS_Events", "TRANSSANS_Histogram"]: - script += f' title="{fulltitle}_TRANS_SANS"' - else: - script += f' title="{fulltitle}_SANS"' - return script + "\n" - - def write_wait_for(self, waitfor, length): - script = "" - if waitfor: - if waitfor.lower() == "MicroAmps".lower(): - script += f" uAhr={length}" - elif waitfor.lower() == "Minutes".lower(): - script += f" min={length}" - elif waitfor.lower() == "Seconds".lower(): - script += f" seconds={length}" - elif waitfor.lower() == "Frames".lower(): - script += f" frame={length}" - else: - script += f" uAhr={length}" - else: - script += f" uAhr={length}" - return script - - def estimate_run_time(self, runs, settings, estimation): - minutes = 0 - if settings.num_sans > 0: - minutes += self.calculate_time(runs, True, estimation.count_rate, estimation.move_time) * settings.num_sans - if settings.num_trans > 0: - minutes += self.calculate_time(runs, False, estimation.count_rate, estimation.move_time) * settings.num_trans - time = datetime(1, 1, 1, 0, 0, 0) + timedelta(minutes=minutes) - return time.strftime("%H:%M:%S") - - @staticmethod - def calculate_time(runs, is_sans, count_rate, seconds_between): - minutes = 0 - for exp in runs: - if exp.position: - if is_sans: - minutes += OpenGenieScriptGenerator.get_minutes(exp.sans_wait, exp.sans, count_rate) - else: - minutes += OpenGenieScriptGenerator.get_minutes(exp.trans_wait, exp.trans, count_rate) - minutes += seconds_between / 60 - return minutes - - @staticmethod - def get_minutes(wait, length, count_rate): - if wait == "Minutes": - return length - elif wait == "Seconds": - return length / 60.0 - elif wait == "Frames": - return length / (25.0 * 60.0) - else: - return length / (count_rate / 60.0) - - - - - - - -if __name__ == "__main__": - runs = [Experiment(position="pos1", trans=10, sans=20, trans_wait="Minutes", sans_wait="Minutes", sample="Sample1", thickness=1.0)] - settings = ExperimentSettings(sample_width="10", sample_height="10", sample_geometry="Flat", order="AllTrans", num_trans=1, num_sans=1, collection_mode="Events", a2_setting_trans="Large", a2_setting_sans="Large", loop_per_run=False) - estimation = Estimation(count_rate=100, move_time=10) - generator = OpenGenieScriptGenerator(is_sans2d=True) - script = generator.generate_script(runs, settings, "my_script") - print(script) - estimated_time = generator.estimate_run_time(runs, settings, estimation) - print(f"Estimated run time: {estimated_time}") \ No newline at end of file diff --git a/Scripting/GeniePythonScriptGenerator.py b/Scripting/ScriptGenerator.py similarity index 97% rename from Scripting/GeniePythonScriptGenerator.py rename to Scripting/ScriptGenerator.py index 51ca3fe..751cb0a 100644 --- a/Scripting/GeniePythonScriptGenerator.py +++ b/Scripting/ScriptGenerator.py @@ -3,7 +3,7 @@ from Models.Experiment import Experiment from Models.ExperimentSettings import ExperimentSettings -class GeniePythonScriptGenerator: +class ScriptGenerator: def __init__(self, is_larmor, is_loq, is_zoom, is_sans2d): self.indent = " " self.is_larmor = is_larmor @@ -215,9 +215,9 @@ def calculate_time(runs, is_sans, count_rate, seconds_between): for exp in runs: if exp.position: if is_sans: - minutes += GeniePythonScriptGenerator.get_minutes(exp.sans_wait, exp.sans, count_rate) + minutes += ScriptGenerator.get_minutes(exp.sans_wait, exp.sans, count_rate) else: - minutes += GeniePythonScriptGenerator.get_minutes(exp.trans_wait, exp.trans, count_rate) + minutes += ScriptGenerator.get_minutes(exp.trans_wait, exp.trans, count_rate) minutes += seconds_between / 60 return minutes @@ -249,7 +249,7 @@ class A2Setting: runs = [Experiment(position="pos1", trans=10, sans=20, trans_wait="Minutes", sans_wait="Minutes", sample="Sample1", thickness=1.0)] settings = ExperimentSettings(sample_width="10", sample_height="10", sample_geometry="Flat", order="AllTrans", num_trans=1, num_sans=1, collection_mode="Events", a2_setting_trans="Large", a2_setting_sans="Large") estimation = Estimation(count_rate=100, move_time=10) - generator = GeniePythonScriptGenerator(is_larmor=True, is_loq=False, is_zoom=False, is_sans2d=False) + generator = ScriptGenerator(is_larmor=True, is_loq=False, is_zoom=False, is_sans2d=False) script = generator.generate_script(runs, settings, "my_script") print(script) estimated_time = generator.estimate_run_time(runs, settings, estimation) diff --git a/SettingsPanel.py b/SettingsPanel.py index 76b1f91..c08cbf4 100644 --- a/SettingsPanel.py +++ b/SettingsPanel.py @@ -29,6 +29,7 @@ def create_widgets(self): # Order ttk.Label(grid_frame, text="Order:").grid(row=0, column=0, sticky=tk.E) + print(self.settings_vm.order_choices) self.com_order = ttk.Combobox(grid_frame, values=self.settings_vm.order_choices) self.com_order.grid(row=0, column=1, columnspan=2, padx=5, pady=5) diff --git a/ViewModels/DataCollection.py b/ViewModels/DataCollection.py deleted file mode 100644 index 40ce416..0000000 --- a/ViewModels/DataCollection.py +++ /dev/null @@ -1,4 +0,0 @@ -class DataCollection: - @staticmethod - def choices(): - return ["Mode1", "Mode2", "Mode3"] \ No newline at end of file diff --git a/ViewModels/Geometry.py b/ViewModels/Geometry.py deleted file mode 100644 index bdc6546..0000000 --- a/ViewModels/Geometry.py +++ /dev/null @@ -1,4 +0,0 @@ -class Geometry: - @staticmethod - def choices(): - return ["Geometry1", "Geometry2", "Geometry3"] \ No newline at end of file diff --git a/ViewModels/InstrumentVM.py b/ViewModels/InstrumentVM.py index 49fb38a..b74bd98 100644 --- a/ViewModels/InstrumentVM.py +++ b/ViewModels/InstrumentVM.py @@ -4,7 +4,7 @@ from Models.InstrumentInfo import InstrumentInfo from Models.Estimation import Estimation from Models.ExperimentSettings import ExperimentSettings -from Scripting.GeniePythonScriptGenerator import GeniePythonScriptGenerator +from Scripting.ScriptGenerator import ScriptGenerator from Properties.Settings import Settings @@ -56,7 +56,7 @@ def set_instrument(self, larmor, loq, sans2d, zoom, a2_label, a2_choices, count_ self.is_sans2d.set(sans2d) self.is_zoom.set(zoom) - InstrumentInfo.script_generator = GeniePythonScriptGenerator(larmor, loq, zoom, sans2d) + InstrumentInfo.script_generator = ScriptGenerator(larmor, loq, zoom, sans2d) InstrumentInfo.a2_label = a2_label InstrumentInfo.a2_choices = a2_choices diff --git a/ViewModels/RunOrder.py b/ViewModels/RunOrder.py deleted file mode 100644 index bec23ec..0000000 --- a/ViewModels/RunOrder.py +++ /dev/null @@ -1,4 +0,0 @@ -class RunOrder: - @staticmethod - def choices(): - return ["Choice1", "Choice2", "Choice3"] \ No newline at end of file diff --git a/ViewModels/SettingsPanelVM.py b/ViewModels/SettingsPanelVM.py index a5a5a2a..7d38c93 100644 --- a/ViewModels/SettingsPanelVM.py +++ b/ViewModels/SettingsPanelVM.py @@ -11,8 +11,9 @@ class SettingsPanelVM: def __init__(self, inst_info=None): self.property_changed = [] - self.order_choices = ["Choice1", "Choice2", "Choice3"] - self.geometry_choices = ["Geometry1", "Geometry2", "Geometry3"] + self.order_choices = RunOrder.get_choices() + self.geometry_choices = Geometry.get_choices() + self.collection_mode_choices = DataCollection.get_choices() self._experiment = ExperimentSettings() @@ -39,18 +40,19 @@ def inst_info(self): def inst_info(self, value): self._inst_info = value self.on_property_changed("inst_info") - + ''' @staticmethod def order_choices(): - return RunOrder.choices() + return RunOrder.get_choices() @staticmethod def geometry_choices(): - return Geometry.choices() - + return Geometry.get_choices() + @staticmethod def collection_mode_choices(): return DataCollection.choices() + ''' From 29e0cf9bc67ab82a8f17526fef83d39e4ec1dbe9 Mon Sep 17 00:00:00 2001 From: Dirk Honecker Date: Mon, 23 Dec 2024 21:16:02 +0000 Subject: [PATCH 49/88] Creating Table with tkintertable --- TableWidget.py | 216 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 216 insertions(+) create mode 100644 TableWidget.py diff --git a/TableWidget.py b/TableWidget.py new file mode 100644 index 0000000..c9c4628 --- /dev/null +++ b/TableWidget.py @@ -0,0 +1,216 @@ + +from tkintertable import * #TableCanvas, TableModel, ColumnHeader, RowHeader + +from tkinter import * +import random +from collections import OrderedDict + +data = {'rec1': {'col1': 99.88, 'col2': 108.79, 'label': 'rec1','col3': 99.88, 'col4': 108.79, 'label2': 'rec1','col5': 99.88, 'col6': 108.79, 'label3': 'rec1'}, + 'rec2': {'col1': 99.88, 'col2': 108.79, 'label': 'rec1','col3': 99.88, 'col4': 108.79, 'label2': 'rec1','col5': 99.88, 'col6': 108.79, 'label3': 'rec1'}, + 'rec3': {'col1': 99.88, 'col2': 108.79, 'label': 'rec1','col3': 99.88, 'col4': 108.79, 'label2': 'rec1','col5': 99.88, 'col6': 108.79, 'label3': 'rec1'}, + 'rec4': {'col1': 99.88, 'col2': 108.79, 'label': 'rec1','col3': 99.88, 'col4': 108.79, 'label2': 'rec1','col5': 99.88, 'col6': 108.79, 'label3': 'rec1'}, + 'rec5': {'col1': 99.88, 'col2': 108.79, 'label': 'rec1','col3': 99.88, 'col4': 108.79, 'label2': 'rec1','col5': 99.88, 'col6': 108.79, 'label3': 'rec1'}, + 'rec6': {'col1': 99.88, 'col2': 108.79, 'label': 'rec1','col3': 99.88, 'col4': 108.79, 'label2': 'rec1','col5': 99.88, 'col6': 108.79, 'label3': 'rec1'}, + 'rec7': {'col1': 99.88, 'col2': 108.79, 'label': 'rec1','col3': 99.88, 'col4': 108.79, 'label2': 'rec1','col5': 99.88, 'col6': 108.79, 'label3': 'rec1'}, + 'rec8': {'col1': 99.88, 'col2': 108.79, 'label': 'rec1','col3': 99.88, 'col4': 108.79, 'label2': 'rec1','col5': 99.88, 'col6': 108.79, 'label3': 'rec1'}, + 'rec9': {'col1': 99.88, 'col2': 108.79, 'label': 'rec1','col3': 99.88, 'col4': 108.79, 'label2': 'rec1','col5': 99.88, 'col6': 108.79, 'label3': 'rec1'} + } + +class CustomRowHeader(RowHeader): + def customCommand(self): + """Define your custom command here""" + print("Custom command executed") + + +class CustomColumnHeader(ColumnHeader): + def popupMenu(self, event): + """Customize the popup menu for the column header""" + + colname = self.model.columnNames[self.table.currentcol] + collabel = self.model.columnlabels[colname] + currcol = self.table.currentcol + popupmenu = Menu(self, tearoff=0) + + def popupFocusOut(event): + popupmenu.unpost() + + # Default actions from the original class + popupmenu.add_command(label="Sort by " + collabel, command=lambda: self.table.sortTable(currcol)) + popupmenu.add_command(label="Sort by " + collabel + ' (descending)', command=lambda: self.table.sortTable(currcol, reverse=1)) + #popupmenu.add_command(label="Delete This Column", command=self.table.deleteColumn) + + # Adding a custom command + #popupmenu.add_command(label="Custom Command", command=self.customCommand) + + popupmenu.bind("", popupFocusOut) + popupmenu.focus_set() + popupmenu.post(event.x_root, event.y_root) + return popupmenu + + def customCommand(self): + """Define your custom command here""" + print("Custom command executed") + + +class CustomTableCanvas(TableCanvas): + + def show(self, callback=None): + """Adds column header and scrollbars and combines them with + the current table adding all to the master frame provided in constructor. + Table is then redrawn.""" + + #Add the table and header to the frame + self.tablerowheader = CustomRowHeader(self.parentframe, self, width=self.rowheaderwidth) + self.tablecolheader = CustomColumnHeader(self.parentframe, self) + self.Yscrollbar = AutoScrollbar(self.parentframe,orient=VERTICAL,command=self.set_yviews) + self.Yscrollbar.grid(row=1,column=2,rowspan=1,sticky='news',pady=0,ipady=0) + self.Xscrollbar = AutoScrollbar(self.parentframe,orient=HORIZONTAL,command=self.set_xviews) + self.Xscrollbar.grid(row=2,column=1,columnspan=1,sticky='news') + self['xscrollcommand'] = self.Xscrollbar.set + self['yscrollcommand'] = self.Yscrollbar.set + self.tablecolheader['xscrollcommand'] = self.Xscrollbar.set + self.tablerowheader['yscrollcommand'] = self.Yscrollbar.set + self.parentframe.rowconfigure(1,weight=1) + self.parentframe.columnconfigure(1,weight=1) + + self.tablecolheader.grid(row=0,column=1,rowspan=1,sticky='news',pady=0,ipady=0) + self.tablerowheader.grid(row=1,column=0,rowspan=1,sticky='news',pady=0,ipady=0) + self.grid(row=1,column=1,rowspan=1,sticky='news',pady=0,ipady=0) + + self.adjustColumnWidths() + self.redrawTable(callback=callback) + self.parentframe.bind("", self.redrawVisible) + self.tablecolheader.xview("moveto", 0) + self.xview("moveto", 0) + return + + def updateModel(self, model): + """Call this method to update the table model""" + + self.model = model + self.rows = self.model.getRowCount() + self.cols = self.model.getColumnCount() + self.tablewidth = (self.cellwidth)*self.cols + self.tablecolheader = CustomColumnHeader(self.parentframe, self) + self.tablerowheader = CustomRowHeader(self.parentframe, self) + self.createTableFrame() + return + + def popupMenu(self, event, rows=None, cols=None, outside=None): + """Add left and right click behaviour for canvas, should not have to override + this function, it will take its values from defined dicts in constructor""" + + # Default actions from the original class + defaultactions = { + "Set Fill Color": lambda: self.setcellColor(rows, cols, key='bg'), + "Set Text Color": lambda: self.setcellColor(rows, cols, key='fg'), + "Copy": lambda: self.copyCell(rows, cols), + "Paste": lambda: self.pasteCell(rows, cols), + "Fill Down": lambda: self.fillDown(rows, cols), + "Fill Right": lambda: self.fillAcross(cols, rows), + "Add Row(s)": lambda: self.addRows(), + "Delete Row(s)": lambda: self.deleteRow(), + "Clear Data": lambda: self.deleteCells(rows, cols), + "Select All": self.select_All, + "New": self.new, + "Load": self.load, + "Save": self.save, + "Import text": self.importTable, + "Export csv": self.exportTable, + "Export Table": self.exportTable, + "Preferences": self.showtablePrefs, + } + + # Adding a custom command + #defaultactions["Custom Command"] = self.customCommand + + main = ["Set Fill Color", "Set Text Color", "Copy", "Paste", "Fill Down", "Fill Right", "Clear Data"] + general = ["Select All", "Add Row(s)", "Delete Row(s)", "Preferences"] + filecommands = ['New', 'Load', 'Save', 'Import text', 'Export csv'] + + def createSubMenu(parent, label, commands): + menu = Menu(parent, tearoff=0) + popupmenu.add_cascade(label=label, menu=menu) + for action in commands: + menu.add_command(label=action, command=defaultactions[action]) + return menu + + def add_commands(fieldtype): + """Add commands to popup menu for column type and specific cell""" + functions = self.columnactions[fieldtype] + for f in functions.keys(): + func = getattr(self, functions[f]) + popupmenu.add_command(label=f, command=lambda: func(row, col)) + return + + popupmenu = Menu(self, tearoff=0) + def popupFocusOut(event): + popupmenu.unpost() + + if outside == None: + # if outside table, just show general items + row = self.get_row_clicked(event) + col = self.get_col_clicked(event) + coltype = self.model.getColumnType(col) + def add_defaultcommands(): + """now add general actions for all cells""" + for action in main: + if action == 'Fill Down' and (rows == None or len(rows) <= 1): + continue + if action == 'Fill Right' and (cols == None or len(cols) <= 1): + continue + else: + popupmenu.add_command(label=action, command=defaultactions[action]) + return + + if coltype in self.columnactions: + add_commands(coltype) + add_defaultcommands() + + for action in general: + popupmenu.add_command(label=action, command=defaultactions[action]) + + popupmenu.add_separator() + popupmenu.bind("", popupFocusOut) + popupmenu.focus_set() + popupmenu.post(event.x_root, event.y_root) + return popupmenu + +# def customCommand(self): +# """Define your custom command here""" +# print("Custom command executed") + +class TableWidget(Frame): + """Basic test frame for the table""" + + def __init__(self, parent=None): + self.parent = parent + Frame.__init__(self) + self.main = self.master + self.main.geometry('800x500+200+100') + self.main.title('Test') + f = Frame(self.main) + f.pack(fill=BOTH,expand=1) + self.table = CustomTableCanvas(f, data=data) + #table.importCSV('test.csv') + print (self.table.model.columnNames) + #table.model.data[1]['a'] = 'XX' + #table.model.setValueAt('YY',0,2) + + self.table.show() + + + + def customOption(self): + print("Custom option selected") + return + + + +if __name__ == "__main__": + + app = TableWidget() + app.mainloop() + + + From fcbb521e657c4ae6e32bedba8697303d3f017e37 Mon Sep 17 00:00:00 2001 From: Dirk Honecker Date: Mon, 23 Dec 2024 23:40:48 +0000 Subject: [PATCH 50/88] Import data from excel file into table widget --- TableWidget.py | 103 ++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 88 insertions(+), 15 deletions(-) diff --git a/TableWidget.py b/TableWidget.py index c9c4628..7798562 100644 --- a/TableWidget.py +++ b/TableWidget.py @@ -2,10 +2,19 @@ from tkintertable import * #TableCanvas, TableModel, ColumnHeader, RowHeader from tkinter import * +from openpyxl import load_workbook import random from collections import OrderedDict -data = {'rec1': {'col1': 99.88, 'col2': 108.79, 'label': 'rec1','col3': 99.88, 'col4': 108.79, 'label2': 'rec1','col5': 99.88, 'col6': 108.79, 'label3': 'rec1'}, +empty_data = { + 'rec1': { + 'col1': '', 'col2': '', 'label': '', 'col3': '', 'col4': '', 'label2': '', 'col5': '', 'col6': '', 'label3': '' + } +} + +data=None +''' +data={'rec1': {'col1': 99.88, 'col2': 108.79, 'label': 'rec1','col3': 99.88, 'col4': 108.79, 'label2': 'rec1','col5': 99.88, 'col6': 108.79, 'label3': 'rec1'}, 'rec2': {'col1': 99.88, 'col2': 108.79, 'label': 'rec1','col3': 99.88, 'col4': 108.79, 'label2': 'rec1','col5': 99.88, 'col6': 108.79, 'label3': 'rec1'}, 'rec3': {'col1': 99.88, 'col2': 108.79, 'label': 'rec1','col3': 99.88, 'col4': 108.79, 'label2': 'rec1','col5': 99.88, 'col6': 108.79, 'label3': 'rec1'}, 'rec4': {'col1': 99.88, 'col2': 108.79, 'label': 'rec1','col3': 99.88, 'col4': 108.79, 'label2': 'rec1','col5': 99.88, 'col6': 108.79, 'label3': 'rec1'}, @@ -15,6 +24,13 @@ 'rec8': {'col1': 99.88, 'col2': 108.79, 'label': 'rec1','col3': 99.88, 'col4': 108.79, 'label2': 'rec1','col5': 99.88, 'col6': 108.79, 'label3': 'rec1'}, 'rec9': {'col1': 99.88, 'col2': 108.79, 'label': 'rec1','col3': 99.88, 'col4': 108.79, 'label2': 'rec1','col5': 99.88, 'col6': 108.79, 'label3': 'rec1'} } +''' + + + + + + class CustomRowHeader(RowHeader): def customCommand(self): @@ -96,37 +112,90 @@ def updateModel(self, model): self.createTableFrame() return + def deleteCells(self, rows=None, cols=None): + """Clear the cell contents""" + + n = messagebox.askyesno("Clear Confirm", + "Clear this data?", + parent=self.parentframe) + if not n: + return + for col in cols: + for row in rows: + #absrow = self.get_AbsoluteRow(row) + self.model.deleteCellRecord(row, col) + self.redrawCell(row,col) + return + + def clearData(self, evt=None): + """Delete cells from gui event""" + + rows = self.multiplerowlist + cols = self.multiplecollist + self.deleteCells(rows, cols) + return + + def fillDown(self, rowlist, collist): + """Fill down a column, or multiple columns""" + model = self.model + #absrow = self.get_AbsoluteRow(rowlist[0]) + #remove first element as we don't want to overwrite it + #rowlist.remove(rowlist[0]) + return + + def load_excel_file(self): + global workbook + filepath = filedialog.askopenfilename(filetypes=[("Excel Files", "*.xlsx")]) + if filepath: + self.workbook = load_workbook(filepath) + sheet_names = self.workbook.sheetnames + self.display_sheet_data(sheet_names[0]) + + def display_sheet_data(self,name=None): + sheet_name = name + if sheet_name and self.workbook: + sheet = self.workbook[sheet_name] + + data={} + for i, row in enumerate(sheet.iter_rows(values_only=True), start=1): + data[f'rec{i}'] = { + 'col1': row[0], 'col2': row[1], 'label': row[2], 'col3': row[3], 'col4': row[4], 'label2': row[5], 'col5': row[6], 'col6': row[7], 'label3': row[8] + } + self.data = data + self.model.importDict(data) + self.redraw() + + def importTable(self): + self.load_excel_file() + def popupMenu(self, event, rows=None, cols=None, outside=None): """Add left and right click behaviour for canvas, should not have to override this function, it will take its values from defined dicts in constructor""" # Default actions from the original class defaultactions = { - "Set Fill Color": lambda: self.setcellColor(rows, cols, key='bg'), - "Set Text Color": lambda: self.setcellColor(rows, cols, key='fg'), + #"Set Fill Color": lambda: self.setcellColor(rows, cols, key='bg'), + #"Set Text Color": lambda: self.setcellColor(rows, cols, key='fg'), "Copy": lambda: self.copyCell(rows, cols), "Paste": lambda: self.pasteCell(rows, cols), "Fill Down": lambda: self.fillDown(rows, cols), - "Fill Right": lambda: self.fillAcross(cols, rows), "Add Row(s)": lambda: self.addRows(), "Delete Row(s)": lambda: self.deleteRow(), - "Clear Data": lambda: self.deleteCells(rows, cols), + "Clear Data": lambda: self.clearData(), "Select All": self.select_All, "New": self.new, - "Load": self.load, - "Save": self.save, - "Import text": self.importTable, - "Export csv": self.exportTable, + "Import Table": self.importTable, "Export Table": self.exportTable, + "Export Reductionbatch": self.exportTable, "Preferences": self.showtablePrefs, } # Adding a custom command #defaultactions["Custom Command"] = self.customCommand - main = ["Set Fill Color", "Set Text Color", "Copy", "Paste", "Fill Down", "Fill Right", "Clear Data"] - general = ["Select All", "Add Row(s)", "Delete Row(s)", "Preferences"] - filecommands = ['New', 'Load', 'Save', 'Import text', 'Export csv'] + main = [ "Copy", "Paste", "Fill Down", "Clear Data"] + general = ["Select All", "Add Row(s)", "Delete Row(s)", "Preferences",'New', 'Import Table', 'Export Table', 'Export Reductionbatch'] + def createSubMenu(parent, label, commands): menu = Menu(parent, tearoff=0) @@ -157,8 +226,6 @@ def add_defaultcommands(): for action in main: if action == 'Fill Down' and (rows == None or len(rows) <= 1): continue - if action == 'Fill Right' and (cols == None or len(cols) <= 1): - continue else: popupmenu.add_command(label=action, command=defaultactions[action]) return @@ -184,6 +251,7 @@ class TableWidget(Frame): """Basic test frame for the table""" def __init__(self, parent=None): + self.data=None self.parent = parent Frame.__init__(self) self.main = self.master @@ -191,7 +259,9 @@ def __init__(self, parent=None): self.main.title('Test') f = Frame(self.main) f.pack(fill=BOTH,expand=1) - self.table = CustomTableCanvas(f, data=data) + if self.data == None: + self.data = empty_data + self.table = CustomTableCanvas(f, data=self.data) #table.importCSV('test.csv') print (self.table.model.columnNames) #table.model.data[1]['a'] = 'XX' @@ -207,6 +277,9 @@ def customOption(self): + + + if __name__ == "__main__": app = TableWidget() From 73d7132de1db4fb0cf72b1789c8a3aefd478132b Mon Sep 17 00:00:00 2001 From: Dirk Honecker Date: Tue, 24 Dec 2024 12:59:16 +0000 Subject: [PATCH 51/88] create new row when pressing enter --- TableWidget.py | 55 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/TableWidget.py b/TableWidget.py index 7798562..8a938f5 100644 --- a/TableWidget.py +++ b/TableWidget.py @@ -168,6 +168,61 @@ def display_sheet_data(self,name=None): def importTable(self): self.load_excel_file() + def do_bindings(self): + """Bind keys and mouse clicks, this can be overriden""" + self.bind("",self.handle_left_click) + self.bind("",self.handle_double_click) + self.bind("", self.handle_left_ctrl_click) + self.bind("", self.handle_left_shift_click) + + self.bind("", self.handle_left_release) + if self.ostyp=='mac': + #For mac we bind Shift, left-click to right click + self.bind("", self.handle_right_click) + self.bind('',self.handle_right_click) + else: + self.bind("", self.handle_right_click) + + self.bind('', self.handle_mouse_drag) + self.bind('', self.handle_motion) + + self.bind_all("", self.deleteRow) + self.bind_all("", self.addRow) + self.bind_all("", self.clearData) + self.bind_all("", self.paste) + + #if not hasattr(self,'parentapp'): + # self.parentapp = self.parentframe + + self.parentframe.master.bind_all("", self.handle_arrow_keys) + self.parentframe.master.bind_all("", self.handle_arrow_keys) + self.parentframe.master.bind_all("", self.handle_arrow_keys) + self.parentframe.master.bind_all("", self.handle_arrow_keys) + self.parentframe.master.bind_all("", self.handle_arrow_keys) + #self.parentframe.master.bind_all("", self.handle_arrow_keys) + self.parentframe.master.bind_all("", self.handle_arrow_keys) + self.parentframe.master.bind_all("", self.move_to_next_row) + + #if 'windows' in self.platform: + self.bind("", self.mouse_wheel) + self.bind('', self.mouse_wheel) + self.bind('', self.mouse_wheel) + self.focus_set() + return + + + def move_to_next_row(self, event): + """Move to the next row when enter is pressed and create a new row if it doesn't exist""" + current_row = self.getSelectedRow() + if current_row >= self.rows - 1: + self.addRow() + self.setSelectedRow(current_row + 1) + self.drawSelectedRect(self.currentrow, self.currentcol) + self.drawSelectedRow() + coltype = self.model.getColumnType(self.currentcol) + if coltype == 'text' or coltype == 'number': + self.drawCellEntry(self.currentrow, self.currentcol) + def popupMenu(self, event, rows=None, cols=None, outside=None): """Add left and right click behaviour for canvas, should not have to override this function, it will take its values from defined dicts in constructor""" From f1c2f91858b80f3f7165dc0409f510b76d52abe6 Mon Sep 17 00:00:00 2001 From: Dirk Honecker Date: Thu, 26 Dec 2024 07:35:03 +0000 Subject: [PATCH 52/88] Initiate table entry list, and constructor for data_list --- App.config.py | 4 +++- TableDataConstructor.py | 46 +++++++++++++++++++++++++++++++++++++++++ TableWidget.py | 39 +++++++++++++++++++--------------- app_config.ini | 1 + 4 files changed, 72 insertions(+), 18 deletions(-) create mode 100644 TableDataConstructor.py diff --git a/App.config.py b/App.config.py index 31cf442..84450c0 100644 --- a/App.config.py +++ b/App.config.py @@ -13,7 +13,9 @@ 'LoopPerRun': 'False', 'A2SettingSans': 'Large = 25, 16 mm', 'A2SettingTrans': 'Large = 25, 16 mm', - 'CollectionMode': 'Events' + 'CollectionMode': 'Events', + 'TableEntries': 'POSITION, TRANS, TRANS_WAIT, SANS, SANS_WAIT, PERIOD, SAMPLE_IDENTIFICATION_STRING, BACKGROUND, THICKNESS, TEMPERATURE, TEMPERATURE2, FIELD, SHEAR_RATE_1, SHEAR_RATE_2, SHEAR_ANGLE_1, SHEAR_ANGLE_2, PRE_COMMAND, POST_COMMAND, RB_NUMBER' + } with open('app_config.ini', 'w') as configfile: diff --git a/TableDataConstructor.py b/TableDataConstructor.py new file mode 100644 index 0000000..313982a --- /dev/null +++ b/TableDataConstructor.py @@ -0,0 +1,46 @@ +class TableDataConstructor: + def __init__(self, table_entries): + self.table_entries = table_entries.split(', ') + + def construct_empty_data(self): + empty_data = { + 'rec1': {entry: '' for entry in self.table_entries} + } + return empty_data + + def construct_data(self, data_list): + data = {} + for i, row in enumerate(data_list, start=1): + data[f'rec{i}'] = {entry: value for entry, value in zip(self.table_entries, row)} + return data + + def deconstruct_data(self, data_dict): + data_list = [] + for key in sorted(data_dict.keys(), key=lambda x: int(x[3:])): + row = [data_dict[key][entry] for entry in self.table_entries] + data_list.append(row) + return data_list + +# Example usage +TableEntries = 'POSITION, TRANS, TRANS_WAIT, SANS, SANS_WAIT, PERIOD, SAMPLE_IDENTIFICATION_STRING, BACKGROUND, THICKNESS, TEMPERATURE, TEMPERATURE2, FIELD, SHEAR_RATE_1, SHEAR_RATE_2, SHEAR_ANGLE_1, SHEAR_ANGLE_2, PRE_COMMAND, POST_COMMAND, RB_NUMBER' +constructor = TableDataConstructor(TableEntries) +empty_data = constructor.construct_empty_data() +print(empty_data) + +# Example usage +TableEntries = 'col1, col2, label, col3, col4, label2, col5, col6, label3' +constructor = TableDataConstructor(TableEntries) + +# Construct data from a list of lists +data_list = [ + [99.88, 108.79, 'rec1', 99.88, 108.79, 'rec1', 99.88, 108.79, 'rec1'], + [99.88, 108.79, 'rec2', 99.88, 108.79, 'rec2', 99.88, 108.79, 'rec2'], + [99.88, 108.79, 'rec3', 99.88, 108.79, 'rec3', 99.88, 108.79, 'rec3']] + # Add more rows as needed + +data = constructor.construct_data(data_list) +print("Data:", data) + +# Deconstruct data back to list of lists +deconstructed_data_list = constructor.deconstruct_data(data) +print("Deconstructed Data List:", deconstructed_data_list) diff --git a/TableWidget.py b/TableWidget.py index 8a938f5..327c9ee 100644 --- a/TableWidget.py +++ b/TableWidget.py @@ -12,8 +12,7 @@ } } -data=None -''' + data={'rec1': {'col1': 99.88, 'col2': 108.79, 'label': 'rec1','col3': 99.88, 'col4': 108.79, 'label2': 'rec1','col5': 99.88, 'col6': 108.79, 'label3': 'rec1'}, 'rec2': {'col1': 99.88, 'col2': 108.79, 'label': 'rec1','col3': 99.88, 'col4': 108.79, 'label2': 'rec1','col5': 99.88, 'col6': 108.79, 'label3': 'rec1'}, 'rec3': {'col1': 99.88, 'col2': 108.79, 'label': 'rec1','col3': 99.88, 'col4': 108.79, 'label2': 'rec1','col5': 99.88, 'col6': 108.79, 'label3': 'rec1'}, @@ -24,7 +23,7 @@ 'rec8': {'col1': 99.88, 'col2': 108.79, 'label': 'rec1','col3': 99.88, 'col4': 108.79, 'label2': 'rec1','col5': 99.88, 'col6': 108.79, 'label3': 'rec1'}, 'rec9': {'col1': 99.88, 'col2': 108.79, 'label': 'rec1','col3': 99.88, 'col4': 108.79, 'label2': 'rec1','col5': 99.88, 'col6': 108.79, 'label3': 'rec1'} } -''' + @@ -303,20 +302,22 @@ def add_defaultcommands(): # print("Custom command executed") class TableWidget(Frame): - """Basic test frame for the table""" + """Frame for the table""" + EMPTY_DATA = { + 'rec1': { + 'col1': '', 'col2': '', 'label': '', 'col3': '', 'col4': '', 'label2': '', 'col5': '', 'col6': '', 'label3': '' + } + } - def __init__(self, parent=None): - self.data=None + def __init__(self, parent=None, data_set=None): + Frame.__init__(self, parent) + print(data_set) + print(TableWidget.EMPTY_DATA) self.parent = parent - Frame.__init__(self) - self.main = self.master - self.main.geometry('800x500+200+100') - self.main.title('Test') - f = Frame(self.main) - f.pack(fill=BOTH,expand=1) - if self.data == None: - self.data = empty_data - self.table = CustomTableCanvas(f, data=self.data) + if data_set is None: + data_set = TableWidget.EMPTY_DATA + self.data = data_set + self.table = CustomTableCanvas(self, data=self.data) #table.importCSV('test.csv') print (self.table.model.columnNames) #table.model.data[1]['a'] = 'XX' @@ -337,8 +338,12 @@ def customOption(self): if __name__ == "__main__": - app = TableWidget() - app.mainloop() + root = Tk() + root.geometry('800x500+200+100') + root.title('Test') + app = TableWidget(root) + app.pack(fill=BOTH, expand=1) + root.mainloop() diff --git a/app_config.ini b/app_config.ini index 1e228bb..a400303 100644 --- a/app_config.ini +++ b/app_config.ini @@ -10,4 +10,5 @@ LoopPerRun = 'False' A2SettingSans = 'Large = 25, 16 mm' A2SettingTrans = 'Large = 25, 16 mm' CollectionMode = 'Events' +TableEntries = 'POSITION, TRANS, TRANS_WAIT, SANS, SANS_WAIT, PERIOD, SAMPLE_IDENTIFICATION_STRING, BACKGROUND, THICKNESS, TEMPERATURE, TEMPERATURE2, FIELD, SHEAR_RATE_1, SHEAR_RATE_2, SHEAR_ANGLE_1, SHEAR_ANGLE_2, PRE_COMMAND, POST_COMMAND, RB_NUMBER' From a4a8b73bd4a0aa2c61d3ad40449fb5754a677f25 Mon Sep 17 00:00:00 2001 From: Dirk Honecker Date: Thu, 26 Dec 2024 13:16:10 +0000 Subject: [PATCH 53/88] import csv --- TableWidget.py | 82 +++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 67 insertions(+), 15 deletions(-) diff --git a/TableWidget.py b/TableWidget.py index 327c9ee..46bf936 100644 --- a/TableWidget.py +++ b/TableWidget.py @@ -5,14 +5,16 @@ from openpyxl import load_workbook import random from collections import OrderedDict +from TableDataConstructor import TableDataConstructor empty_data = { 'rec1': { 'col1': '', 'col2': '', 'label': '', 'col3': '', 'col4': '', 'label2': '', 'col5': '', 'col6': '', 'label3': '' } } - - +''' +data=None +''' data={'rec1': {'col1': 99.88, 'col2': 108.79, 'label': 'rec1','col3': 99.88, 'col4': 108.79, 'label2': 'rec1','col5': 99.88, 'col6': 108.79, 'label3': 'rec1'}, 'rec2': {'col1': 99.88, 'col2': 108.79, 'label': 'rec1','col3': 99.88, 'col4': 108.79, 'label2': 'rec1','col5': 99.88, 'col6': 108.79, 'label3': 'rec1'}, 'rec3': {'col1': 99.88, 'col2': 108.79, 'label': 'rec1','col3': 99.88, 'col4': 108.79, 'label2': 'rec1','col5': 99.88, 'col6': 108.79, 'label3': 'rec1'}, @@ -66,6 +68,48 @@ def customCommand(self): """Define your custom command here""" print("Custom command executed") +class TableImporter: + """Provides import utility methods for the Table and Table Model classes""" + + def __init__(self): + """Setup globals""" + self.separator_list = [',', ' ', '\t', ':'] + self.var_sep = StringVar() + self.var_sep.set(',') + return + + @staticmethod + def ImportTableData(table, sep=None): + """Import table data from a comma separated file and populate the table model""" + + parent = table.parentframe + filename = filedialog.askopenfilename(parent=parent, defaultextension='.csv', + filetypes=[("CSV files", "*.csv")]) + if not filename: + return + + if sep is None: + sep = ',' + + if not os.path.isfile(filename): + return None + + dictdata = {} + try: + with open(filename, "r") as csvfile: + reader = csv.DictReader(csvfile, delimiter=sep) + for count, rec in enumerate(reader): + dictdata[count] = rec + except Exception as e: + messagebox.showerror("Import Error", f"Failed to import data: {str(e)}") + return None + + model = table.getModel() + model.deleteRows() + model.importDict(dictdata) + table.redrawTable() + return dictdata + class CustomTableCanvas(TableCanvas): @@ -154,18 +198,24 @@ def display_sheet_data(self,name=None): sheet_name = name if sheet_name and self.workbook: sheet = self.workbook[sheet_name] - - data={} - for i, row in enumerate(sheet.iter_rows(values_only=True), start=1): - data[f'rec{i}'] = { - 'col1': row[0], 'col2': row[1], 'label': row[2], 'col3': row[3], 'col4': row[4], 'label2': row[5], 'col5': row[6], 'col6': row[7], 'label3': row[8] - } + TableEntries = 'col1, col2, label, col3, col4, label2, col5, col6, label3' + constructor = TableDataConstructor(TableEntries) + data = constructor.construct_data(sheet.iter_rows(values_only=True)) self.data = data self.model.importDict(data) self.redraw() def importTable(self): - self.load_excel_file() + #self.load_excel_file() + self.importCSV() + + + def importCSV(self, filename=None, sep=','): + """Import from csv file""" + + importer = TableImporter() + importer.ImportTableData(self) + return def do_bindings(self): """Bind keys and mouse clicks, this can be overriden""" @@ -309,14 +359,14 @@ class TableWidget(Frame): } } - def __init__(self, parent=None, data_set=None): + def __init__(self, parent=None, data=None): Frame.__init__(self, parent) - print(data_set) + print(data) print(TableWidget.EMPTY_DATA) self.parent = parent - if data_set is None: - data_set = TableWidget.EMPTY_DATA - self.data = data_set + if data is None: + data = TableWidget.EMPTY_DATA + self.data = data self.table = CustomTableCanvas(self, data=self.data) #table.importCSV('test.csv') print (self.table.model.columnNames) @@ -341,7 +391,9 @@ def customOption(self): root = Tk() root.geometry('800x500+200+100') root.title('Test') - app = TableWidget(root) + if data==None: + data = TableWidget.EMPTY_DATA + app = TableWidget(root, data=data) app.pack(fill=BOTH, expand=1) root.mainloop() From 4e1f15a274b4a84a94dd8603668074838e6a82ca Mon Sep 17 00:00:00 2001 From: Dirk Honecker Date: Thu, 26 Dec 2024 21:59:55 +0000 Subject: [PATCH 54/88] show default values in gui --- App.config.py | 5 ++-- EstimationPanel.py | 4 +++ MainWindow.py | 49 ++++++++++++-------------------- Models/A2Setting.py | 1 - Models/ExperimentSettings.py | 53 ++++++++++++++++++----------------- Models/RunOrder.py | 4 +-- Properties/Settings.ini | 21 ++++++++------ Properties/Settings.py | 18 ++++++++++++ Scripting/ScriptGenerator.py | 20 ++++--------- SettingsPanel.py | 24 ++++++++++++++-- TableWidget.py | 41 +++++++-------------------- ViewModels/A2Setting.py | 6 ---- ViewModels/SettingsPanelVM.py | 4 +-- app_config.ini | 17 +++++------ 14 files changed, 133 insertions(+), 134 deletions(-) delete mode 100644 ViewModels/A2Setting.py diff --git a/App.config.py b/App.config.py index 84450c0..a8c5a2b 100644 --- a/App.config.py +++ b/App.config.py @@ -14,8 +14,9 @@ 'A2SettingSans': 'Large = 25, 16 mm', 'A2SettingTrans': 'Large = 25, 16 mm', 'CollectionMode': 'Events', - 'TableEntries': 'POSITION, TRANS, TRANS_WAIT, SANS, SANS_WAIT, PERIOD, SAMPLE_IDENTIFICATION_STRING, BACKGROUND, THICKNESS, TEMPERATURE, TEMPERATURE2, FIELD, SHEAR_RATE_1, SHEAR_RATE_2, SHEAR_ANGLE_1, SHEAR_ANGLE_2, PRE_COMMAND, POST_COMMAND, RB_NUMBER' - + 'TableEntries': 'POSITION, TRANS, TRANS_WAIT, SANS, SANS_WAIT, PERIOD, SAMPLE_IDENTIFICATION_STRING, BACKGROUND, THICKNESS, TEMPERATURE, TEMPERATURE2, FIELD, SHEAR_RATE_1, SHEAR_RATE_2, SHEAR_ANGLE_1, SHEAR_ANGLE_2, PRE_COMMAND, POST_COMMAND, RB_NUMBER', + 'count_rate': '38', + 'move_time': '120' } with open('app_config.ini', 'w') as configfile: diff --git a/EstimationPanel.py b/EstimationPanel.py index 6372d99..064087e 100644 --- a/EstimationPanel.py +++ b/EstimationPanel.py @@ -2,6 +2,7 @@ from tkinter import ttk from ViewModels.EstimationPanelVM import EstimationPanelVM from Models.Estimation import Estimation +from Properties.Settings import Settings class EstimationPanel(ttk.Frame): def __init__(self, parent, estimate = None): @@ -11,6 +12,7 @@ def __init__(self, parent, estimate = None): self.create_widgets() def create_widgets(self): + settings = Settings() #self.pack(fill=tk.BOTH, expand=True) self.grid(sticky="nsew") @@ -26,6 +28,7 @@ def create_widgets(self): count_rate_frame = ttk.Frame(grid_frame) count_rate_frame.grid(row=0, column=1, columnspan=2) self.txt_count_rate = ttk.Entry(count_rate_frame, width=10, textvariable=self.estimate.count_rate) + self.txt_count_rate.insert(0, settings.count_rate) # Default value self.txt_count_rate.grid(row=0, column=0, padx=5, pady=5) ttk.Label(count_rate_frame, text="µA/hr").grid(row=0, column=1) @@ -34,6 +37,7 @@ def create_widgets(self): move_time_frame = ttk.Frame(grid_frame) move_time_frame.grid(row=1, column=1, columnspan=2) self.txt_move_time = ttk.Entry(move_time_frame, width=10, textvariable=self.estimate.move_time) + self.txt_move_time.insert(0, settings.move_time) # Default value self.txt_move_time.grid(row=0, column=0, padx=5, pady=5) ttk.Label(move_time_frame, text="seconds").grid(row=0, column=1) diff --git a/MainWindow.py b/MainWindow.py index bde4c06..1188ad0 100644 --- a/MainWindow.py +++ b/MainWindow.py @@ -13,7 +13,11 @@ from Dialogs.PreviewDialog import PreviewDialog from SettingsPanel import SettingsPanel from EstimationPanel import EstimationPanel +from TableWidget import TableWidget +from Properties.Settings import Settings +TableEntries = 'POSITION, TRANS, TRANS_WAIT, SANS, SANS_WAIT, PERIOD, SAMPLE_IDENTIFICATION_STRING, BACKGROUND, THICKNESS, TEMPERATURE, TEMPERATURE2, FIELD, SHEAR_RATE_1, SHEAR_RATE_2, SHEAR_ANGLE_1, SHEAR_ANGLE_2, PRE_COMMAND, POST_COMMAND, RB_NUMBER' + class MainWindow(tk.Frame): def __init__(self,parent): @@ -31,47 +35,25 @@ def __init__(self,parent): self.create_widgets() self.load_last_instrument() + def create_widgets(self): #self.data_grid = ttk.Treeview(self) self.grid_columnconfigure(1, weight=1) self.grid_rowconfigure(1, weight=1) - - - - # Data Grid with horizontal and vertical scrollbars + + self.data_frame = TableWidget(self) + #self.data_frame.grid(row=0, column=1, rowspan=4, sticky="nsew", padx=5, pady=5) + self.data_frame.grid(row=0, column=1, sticky="nsew", padx=5, pady=5) ''' - self.canvas = tk.Canvas(self, borderwidth=0, background="#ffffff") - self.data_frame = tk.Frame(self.canvas, background="#f0ffff") - #data_frame = ttk.Frame(self) - self.scrolly = tk.Scrollbar(self, orient="vertical", command=self.canvas.yview) - self.scrollx = tk.Scrollbar(self, orient="horizontal", command=self.canvas.xview) - self.canvas.configure(xscrollcommand=self.scrollx.set) - self.canvas.configure(yscrollcommand=self.scrolly.set) - self.canvas.create_window((4,4), window=self.data_frame, anchor="nw", tags="self.frame") - - self.data_frame.bind("", self.onFrameConfigure) - ''' - - self.data_frame = ttk.Frame(self) - self.data_frame.grid(row=0, column=1, rowspan=4, sticky="nsew", padx=5, pady=5) - self.data_grid = ttk.Treeview(self.data_frame, columns=("POSITION", "TRANS", "TRANS_WAIT", "SANS", "SANS_WAIT", "PERIOD", "SAMPLE_IDENTIFICATION_STRING", "BACKGROUND", "THICKNESS", "TEMPERATURE", "TEMPERATURE2", "FIELD", "SHEAR_RATE_1", "SHEAR_RATE_2", "SHEAR_ANGLE_1", "SHEAR_ANGLE_2", "PRE_COMMAND", "POST_COMMAND", "RB_NUMBER"), show="headings") - self.data_grid.grid(row=0, column=0, sticky="nsew") - scrollbar_y = ttk.Scrollbar(self.data_frame, orient=tk.VERTICAL, command=self.data_grid.yview) - scrollbar_y.grid(row=0, column=1, sticky="ns") - self.data_grid.configure(yscrollcommand=scrollbar_y.set) - - scrollbar_x = ttk.Scrollbar(self.data_frame, orient=tk.HORIZONTAL, command=self.data_grid.xview) - scrollbar_x.grid(row=1, column=0, sticky="ew") - self.data_grid.configure(xscrollcommand=scrollbar_x.set) - - for col in self.data_grid["columns"]: self.data_grid.heading(col, text=col.replace("_", " ")) + ''' + # Settings Panel self.pnl_settings = SettingsPanel(self, self.settings) self.pnl_settings.grid(row=0, column=0, sticky="ew") @@ -112,9 +94,12 @@ def onFrameConfigure(self, event): self.canvas.configure(scrollregion=self.canvas.bbox("all")) ''' def load_last_instrument(self): - inst = "LARMOR" # Replace with actual settings retrieval + settings = Settings() + inst = settings.last_instrument # Replace with actual settings retrieval self.instrument_var.set(inst) + + def pnl_settings_on_write_clicked(self): filetypes = [("genie_python Files", "*.py")] if self.instrument_var.get() in ["LARMOR", "LOQ", "ZOOM", "SANS2D"] else [("Open Genie Files", "*.gcl")] filepath = filedialog.asksaveasfilename(defaultextension=filetypes[0][1], filetypes=filetypes) @@ -175,6 +160,8 @@ def on_close(self): else: last_instrument = "LARMOR" # Save last_instrument to settings + settings = Settings() + settings.last_instrument(last_instrument) self.destroy() @@ -188,5 +175,5 @@ def on_close(self): app.grid(sticky="nsew") root.grid_rowconfigure(0, weight=1) root.grid_columnconfigure(0, weight=1) - app.protocol("WM_DELETE_WINDOW", app.on_close) + #app.protocol("WM_DELETE_WINDOW", app.on_close) app.mainloop() \ No newline at end of file diff --git a/Models/A2Setting.py b/Models/A2Setting.py index 7a55dee..51591d5 100644 --- a/Models/A2Setting.py +++ b/Models/A2Setting.py @@ -4,7 +4,6 @@ class A2Setting: MEDIUM = "Medium = 12 mm" SMALL = "Small = 5/1 mm" TRANS = "Trans = 1 mm" - LARMOR = "" LARMOR_LARGE = "Large = 25, 16 mm" LARMOR_MEDIUM = "Medium = 20, 14 mm" LARMOR_SMALL = "Small = 15, 10 mm" diff --git a/Models/ExperimentSettings.py b/Models/ExperimentSettings.py index c448c54..1a65435 100644 --- a/Models/ExperimentSettings.py +++ b/Models/ExperimentSettings.py @@ -1,23 +1,22 @@ import tkinter as tk -from configparser import ConfigParser +from Properties.Settings import Settings class ExperimentSettings: - def __init__(self, config_file='settings.ini'): - self.config = ConfigParser() - self.config.read(config_file) - self.config_file = config_file - - self._order = tk.StringVar(value=self.config['DEFAULT'].get('RunOrder', 'TransFirst')) - self._num_sans = tk.IntVar(value=self.config['DEFAULT'].getint('NumSans', 1)) - self._num_trans = tk.IntVar(value=self.config['DEFAULT'].getint('NumTrans', 1)) - self._loop_per_run = tk.BooleanVar(value=self.config['DEFAULT'].getboolean('LoopPerRun', False)) - self._a2_setting_sans = tk.StringVar(value=self.config['DEFAULT'].get('A2SettingSans', 'Large')) - self._a2_setting_trans = tk.StringVar(value=self.config['DEFAULT'].get('A2SettingTrans', 'Large')) - self._sample_geometry = tk.StringVar(value=self.config['DEFAULT'].get('Geometry', 'Flat Plate')) - self._sample_height = tk.DoubleVar(value=self.config['DEFAULT'].getfloat('SampleHeight', 11)) - self._sample_width = tk.DoubleVar(value=self.config['DEFAULT'].getfloat('SampleWidth', 11)) + def __init__(self): + settings = Settings() + + self._order = tk.StringVar(value=settings.run_order) + self._num_sans = tk.IntVar(value=settings.num_sans) + self._num_trans = tk.IntVar(value=settings.num_trans) + self._loop_per_run = tk.BooleanVar(value=settings.loop_per_run) + self._a2_setting_sans = tk.StringVar(value=settings.a2_setting_sans) + self._a2_setting_trans = tk.StringVar(value=settings.a2_setting_trans) + self._sample_geometry = tk.StringVar(value=settings.geometry) + self._sample_height = tk.DoubleVar(value=settings.sample_height) + self._sample_width = tk.DoubleVar(value=settings.sample_width) self._sample_width_enabled = tk.BooleanVar(value=True) - self._collection_mode = tk.StringVar(value=self.config['DEFAULT'].get('CollectionMode', 'Events')) + + self._collection_mode = tk.StringVar(value=settings.collection_mode) self._is_valid = tk.BooleanVar(value=True) self.property_changed = [] @@ -38,16 +37,18 @@ def on_property_changed(self, *args): self.save() def save(self): - self.config['DEFAULT']['RunOrder'] = self._order.get() - self.config['DEFAULT']['NumSans'] = str(self._num_sans.get()) - self.config['DEFAULT']['NumTrans'] = str(self._num_trans.get()) - self.config['DEFAULT']['LoopPerRun'] = str(self._loop_per_run.get()) - self.config['DEFAULT']['A2SettingSans'] = self._a2_setting_sans.get() - self.config['DEFAULT']['A2SettingTrans'] = self._a2_setting_trans.get() - self.config['DEFAULT']['Geometry'] = self._sample_geometry.get() - self.config['DEFAULT']['SampleHeight'] = str(self._sample_height.get()) - self.config['DEFAULT']['SampleWidth'] = str(self._sample_width.get()) - self.config['DEFAULT']['CollectionMode'] = self._collection_mode.get() + settings = Settings() + settings.run_order(str(self._order.get())) + settings.num_sans(str(self._num_sans.get())) + settings.num_sans(str(self._num_trans.get())) + settings.loop_per_run(str(self._loop_per_run.get())) + print(self._a2_setting_sans.get()) + settings.a2_setting_sans(self._a2_setting_sans.get()) + settings.a2_setting_trans(self._a2_setting_trans.get()) + settings.geometry(self._sample_geometry.get()) + settings.sample_height(str(self._sample_height.get())) + settings.sample_width(str(self._sample_width.get())) + settings.collection_mode(self._collection_mode.get()) with open(self.config_file, 'w') as configfile: self.config.write(configfile) diff --git a/Models/RunOrder.py b/Models/RunOrder.py index 73e5815..ea95720 100644 --- a/Models/RunOrder.py +++ b/Models/RunOrder.py @@ -4,12 +4,12 @@ ALL_SANS = "All SANS first" SANS_FIRST = "Alternate - SANS first" - choices = [ + choices = ( ALL_TRANS, TRANS_FIRST, ALL_SANS, SANS_FIRST - ] + ) @staticmethod def get_choices(): diff --git a/Properties/Settings.ini b/Properties/Settings.ini index 37ce0f0..9fff4e1 100644 --- a/Properties/Settings.ini +++ b/Properties/Settings.ini @@ -1,13 +1,16 @@ [DEFAULT] lastinstrument = LOQ -sampleheight = '11' -samplewidth = '11' -geometry = 'Flat Plate' -runorder = 'Alternate - TRANS first' -numtrans = '1' -numsans = '1' -loopperrun = 'False' -a2settingsans = -a2settingtrans = +sampleheight = 11 +samplewidth = 11 +geometry = Flat Plate +runorder = Alternate - TRANS first +numtrans = 1 +numsans = 1 +loopperrun = False +a2settingsans = +a2settingtrans = collectionmode = 'Events' +tableentries = 'POSITION, TRANS, TRANS_WAIT, SANS, SANS_WAIT, PERIOD, SAMPLE_IDENTIFICATION_STRING, BACKGROUND, THICKNESS, TEMPERATURE, TEMPERATURE2, FIELD, SHEAR_RATE_1, SHEAR_RATE_2, SHEAR_ANGLE_1, SHEAR_ANGLE_2, PRE_COMMAND, POST_COMMAND, RB_NUMBER' +count_rate = 38 +move_time = 120 diff --git a/Properties/Settings.py b/Properties/Settings.py index 6d9f60f..0b9b36f 100644 --- a/Properties/Settings.py +++ b/Properties/Settings.py @@ -108,6 +108,24 @@ def collection_mode(self, value): self.dict_settings.set('DEFAULT','CollectionMode', str(value)) self.save() + @property + def count_rate(self): + return self.dict_settings.get('DEFAULT','count_rate') + + @count_rate.setter + def count_rate(self, value): + self.dict_settings.set('DEFAULT','count_rate', str(value)) + self.save() + + @property + def move_time(self): + return self.dict_settings.get('DEFAULT','move_time') + + @move_time.setter + def move_time(self, value): + self.dict_settings.set('DEFAULT','move_time', str(value)) + self.save() + def save(self): with open(self.setting_ini_path, 'w') as configfile: self.dict_settings.write(configfile) diff --git a/Scripting/ScriptGenerator.py b/Scripting/ScriptGenerator.py index 751cb0a..fcee1a5 100644 --- a/Scripting/ScriptGenerator.py +++ b/Scripting/ScriptGenerator.py @@ -2,6 +2,7 @@ from Models.Estimation import Estimation from Models.Experiment import Experiment from Models.ExperimentSettings import ExperimentSettings +from Models.A2Setting import A2Setting class ScriptGenerator: def __init__(self, is_larmor, is_loq, is_zoom, is_sans2d): @@ -154,13 +155,14 @@ def do_sans_run(self, script, exp, indent, settings): return script def aperture_string(self, size): - if size in [A2Setting.Large, A2Setting.Larmor]: + a2setting = A2Setting() + if size in [a2setting.Large, a2setting.Larmor]: return "large" - elif size in [A2Setting.Medium, A2Setting.Larmor]: + elif size in [a2setting.Medium, a2setting.Larmor]: return "medium" - elif size in [A2Setting.Small, A2Setting.Larmor]: + elif size in [a2setting.Small, a2setting.Larmor]: return "small" - elif size in [A2Setting.XLarge, A2Setting.Larmor]: + elif size in [a2setting.XLarge, a2setting.Larmor]: return "xlarge" return "" @@ -232,16 +234,6 @@ def get_minutes(wait, length, count_rate): else: return length / (count_rate / 60.0) -class A2Setting: - Large = "Large" - Medium = "Medium" - Small = "Small" - XLarge = "XLarge" - Larmor = "Larmor" - - - - diff --git a/SettingsPanel.py b/SettingsPanel.py index c08cbf4..2199f0d 100644 --- a/SettingsPanel.py +++ b/SettingsPanel.py @@ -1,7 +1,7 @@ import tkinter as tk from tkinter import ttk from ViewModels.SettingsPanelVM import SettingsPanelVM - +from Properties.Settings import Settings class SettingsPanel(ttk.Frame): def __init__(self, parent, settings_vm=None): @@ -19,7 +19,7 @@ def create_widgets(self): #self.write_button = ttk.Button(self, text="Write Script", command=self.on_write_clicked) #self.write_button.pack(side=tk.LEFT, padx=5, pady=5) - + settings = Settings() label = ttk.Label(self, text="Settings", font=("Helvetica", 16, "bold")) label.grid(row=0, column=0, columnspan=2, pady=10) @@ -30,14 +30,30 @@ def create_widgets(self): # Order ttk.Label(grid_frame, text="Order:").grid(row=0, column=0, sticky=tk.E) print(self.settings_vm.order_choices) + print(settings.run_order) + self.com_order = ttk.Combobox(grid_frame, values=self.settings_vm.order_choices) + self.com_order['values']=self.settings_vm.order_choices self.com_order.grid(row=0, column=1, columnspan=2, padx=5, pady=5) + self.com_order.set(settings.run_order) # Set default value + ''' + # Collection Mode + ttk.Label(grid_frame, text="Collection Mode:").grid(row=0, column=0, sticky=tk.E) + print(self.settings_vm.collection_mode_choices) + print(settings.collection_mode) + + self.collect_mode = ttk.Combobox(grid_frame, values=self.settings_vm.collection_mode_choices) + self.collect_mode['values']=self.settings_vm.collection_mode_choices + self.collect_mode.grid(row=1, column=1, columnspan=2, padx=5, pady=5) + self.collect_mode.set(settings.collection_mode) # Set default value + ''' # Do SANS ttk.Label(grid_frame, text="Do SANS:").grid(row=1, column=0, sticky=tk.E) sans_frame = ttk.Frame(grid_frame) sans_frame.grid(row=1, column=1, columnspan=2) self.txt_times_sans = ttk.Entry(sans_frame, width=5) + self.txt_times_sans.insert(0, settings.num_sans) # Default value self.txt_times_sans.grid(row=0, column=0, padx=5, pady=5) ttk.Label(sans_frame, text="time(s)").grid(row=0, column=1) @@ -46,6 +62,7 @@ def create_widgets(self): trans_frame = ttk.Frame(grid_frame) trans_frame.grid(row=2, column=1, columnspan=2) self.txt_times_trans = ttk.Entry(trans_frame, width=5) + self.txt_times_trans.insert(0, settings.num_trans) # Default value self.txt_times_trans.grid(row=0, column=0, padx=5, pady=5) ttk.Label(trans_frame, text="time(s)").grid(row=0, column=1) @@ -57,12 +74,14 @@ def create_widgets(self): ttk.Label(grid_frame, text="Sample Geometry:").grid(row=6, column=0, sticky=tk.E) self.com_sample_geo = ttk.Combobox(grid_frame, values=self.settings_vm.geometry_choices) self.com_sample_geo.grid(row=6, column=1, columnspan=2, padx=5, pady=5) + self.com_sample_geo.set(settings.geometry) # Set default value # Sample Height ttk.Label(grid_frame, text="Sample Height:").grid(row=7, column=0, sticky=tk.E) sample_height_frame = ttk.Frame(grid_frame) sample_height_frame.grid(row=7, column=1, columnspan=2) self.txt_sample_height = ttk.Entry(sample_height_frame, width=5) + self.txt_sample_height.insert(0, settings.sample_height) # Default value self.txt_sample_height.grid(row=0, column=0, padx=5, pady=5) ttk.Label(sample_height_frame, text="mm").grid(row=0, column=1) @@ -71,6 +90,7 @@ def create_widgets(self): sample_width_frame = ttk.Frame(grid_frame) sample_width_frame.grid(row=8, column=1, columnspan=2) self.txt_sample_width = ttk.Entry(sample_width_frame, width=5) + self.txt_sample_width.insert(0, settings.sample_width) # Default value self.txt_sample_width.grid(row=0, column=0, padx=5, pady=5) ttk.Label(sample_width_frame, text="mm").grid(row=0, column=1) diff --git a/TableWidget.py b/TableWidget.py index 46bf936..ce17bcf 100644 --- a/TableWidget.py +++ b/TableWidget.py @@ -7,30 +7,10 @@ from collections import OrderedDict from TableDataConstructor import TableDataConstructor -empty_data = { - 'rec1': { - 'col1': '', 'col2': '', 'label': '', 'col3': '', 'col4': '', 'label2': '', 'col5': '', 'col6': '', 'label3': '' - } -} -''' -data=None -''' -data={'rec1': {'col1': 99.88, 'col2': 108.79, 'label': 'rec1','col3': 99.88, 'col4': 108.79, 'label2': 'rec1','col5': 99.88, 'col6': 108.79, 'label3': 'rec1'}, - 'rec2': {'col1': 99.88, 'col2': 108.79, 'label': 'rec1','col3': 99.88, 'col4': 108.79, 'label2': 'rec1','col5': 99.88, 'col6': 108.79, 'label3': 'rec1'}, - 'rec3': {'col1': 99.88, 'col2': 108.79, 'label': 'rec1','col3': 99.88, 'col4': 108.79, 'label2': 'rec1','col5': 99.88, 'col6': 108.79, 'label3': 'rec1'}, - 'rec4': {'col1': 99.88, 'col2': 108.79, 'label': 'rec1','col3': 99.88, 'col4': 108.79, 'label2': 'rec1','col5': 99.88, 'col6': 108.79, 'label3': 'rec1'}, - 'rec5': {'col1': 99.88, 'col2': 108.79, 'label': 'rec1','col3': 99.88, 'col4': 108.79, 'label2': 'rec1','col5': 99.88, 'col6': 108.79, 'label3': 'rec1'}, - 'rec6': {'col1': 99.88, 'col2': 108.79, 'label': 'rec1','col3': 99.88, 'col4': 108.79, 'label2': 'rec1','col5': 99.88, 'col6': 108.79, 'label3': 'rec1'}, - 'rec7': {'col1': 99.88, 'col2': 108.79, 'label': 'rec1','col3': 99.88, 'col4': 108.79, 'label2': 'rec1','col5': 99.88, 'col6': 108.79, 'label3': 'rec1'}, - 'rec8': {'col1': 99.88, 'col2': 108.79, 'label': 'rec1','col3': 99.88, 'col4': 108.79, 'label2': 'rec1','col5': 99.88, 'col6': 108.79, 'label3': 'rec1'}, - 'rec9': {'col1': 99.88, 'col2': 108.79, 'label': 'rec1','col3': 99.88, 'col4': 108.79, 'label2': 'rec1','col5': 99.88, 'col6': 108.79, 'label3': 'rec1'} - } - - - - - +TableEntries = 'POSITION, TRANS, TRANS_WAIT, SANS, SANS_WAIT, PERIOD, SAMPLE_IDENTIFICATION_STRING, BACKGROUND, THICKNESS, TEMPERATURE, TEMPERATURE2, FIELD, SHEAR_RATE_1, SHEAR_RATE_2, SHEAR_ANGLE_1, SHEAR_ANGLE_2, PRE_COMMAND, POST_COMMAND, RB_NUMBER' + +data=None class CustomRowHeader(RowHeader): @@ -198,7 +178,7 @@ def display_sheet_data(self,name=None): sheet_name = name if sheet_name and self.workbook: sheet = self.workbook[sheet_name] - TableEntries = 'col1, col2, label, col3, col4, label2, col5, col6, label3' + #TableEntries = 'col1, col2, label, col3, col4, label2, col5, col6, label3' constructor = TableDataConstructor(TableEntries) data = constructor.construct_data(sheet.iter_rows(values_only=True)) self.data = data @@ -353,16 +333,15 @@ def add_defaultcommands(): class TableWidget(Frame): """Frame for the table""" - EMPTY_DATA = { - 'rec1': { - 'col1': '', 'col2': '', 'label': '', 'col3': '', 'col4': '', 'label2': '', 'col5': '', 'col6': '', 'label3': '' - } - } + #TableEntries = 'col1, col2, label, col3, col4, label2, col5, col6, label3' + + constructor = TableDataConstructor(TableEntries) + EMPTY_DATA = constructor.construct_empty_data() + def __init__(self, parent=None, data=None): Frame.__init__(self, parent) - print(data) - print(TableWidget.EMPTY_DATA) + self.parent = parent if data is None: data = TableWidget.EMPTY_DATA diff --git a/ViewModels/A2Setting.py b/ViewModels/A2Setting.py deleted file mode 100644 index 773a782..0000000 --- a/ViewModels/A2Setting.py +++ /dev/null @@ -1,6 +0,0 @@ -class A2Setting: - Large = "Large" - Medium = "Medium" - Small = "Small" - XLarge = "XLarge" - Larmor = "Larmor" \ No newline at end of file diff --git a/ViewModels/SettingsPanelVM.py b/ViewModels/SettingsPanelVM.py index 7d38c93..420385f 100644 --- a/ViewModels/SettingsPanelVM.py +++ b/ViewModels/SettingsPanelVM.py @@ -40,7 +40,7 @@ def inst_info(self): def inst_info(self, value): self._inst_info = value self.on_property_changed("inst_info") - ''' + @staticmethod def order_choices(): return RunOrder.get_choices() @@ -52,7 +52,7 @@ def geometry_choices(): @staticmethod def collection_mode_choices(): return DataCollection.choices() - ''' + diff --git a/app_config.ini b/app_config.ini index a400303..38503b7 100644 --- a/app_config.ini +++ b/app_config.ini @@ -1,14 +1,15 @@ [DEFAULT] LastInstrument = 'LARMOR' -SampleHeight = '11' -SampleWidth = '11' -Geometry = 'Flat Plate' -RunOrder = 'Alternate - TRANS first' -NumTrans = '1' -NumSans = '1' -LoopPerRun = 'False' +SampleHeight = 11 +SampleWidth = 11 +Geometry = Flat Plate +RunOrder = Alternate - TRANS first +NumTrans = 1 +NumSans = 1 +LoopPerRun = False A2SettingSans = 'Large = 25, 16 mm' A2SettingTrans = 'Large = 25, 16 mm' CollectionMode = 'Events' TableEntries = 'POSITION, TRANS, TRANS_WAIT, SANS, SANS_WAIT, PERIOD, SAMPLE_IDENTIFICATION_STRING, BACKGROUND, THICKNESS, TEMPERATURE, TEMPERATURE2, FIELD, SHEAR_RATE_1, SHEAR_RATE_2, SHEAR_ANGLE_1, SHEAR_ANGLE_2, PRE_COMMAND, POST_COMMAND, RB_NUMBER' - +Count_Rate=38 +Move_Time=120 From 023e1fb356a6d0b0eef9f0924f1cd7668bb4741b Mon Sep 17 00:00:00 2001 From: Dirk Honecker Date: Fri, 27 Dec 2024 00:06:28 +0000 Subject: [PATCH 55/88] Fix tracking of instrument changes --- MainWindow.py | 19 +++++-------- Models/InstrumentInfo.py | 4 +++ Scripting/ScriptGenerator.py | 26 +++++++++--------- ViewModels/InstrumentVM.py | 52 +++++++++++++----------------------- 4 files changed, 41 insertions(+), 60 deletions(-) diff --git a/MainWindow.py b/MainWindow.py index 1188ad0..e3af20c 100644 --- a/MainWindow.py +++ b/MainWindow.py @@ -15,6 +15,7 @@ from EstimationPanel import EstimationPanel from TableWidget import TableWidget from Properties.Settings import Settings +from Scripting.ScriptGenerator import ScriptGenerator TableEntries = 'POSITION, TRANS, TRANS_WAIT, SANS, SANS_WAIT, PERIOD, SAMPLE_IDENTIFICATION_STRING, BACKGROUND, THICKNESS, TEMPERATURE, TEMPERATURE2, FIELD, SHEAR_RATE_1, SHEAR_RATE_2, SHEAR_ANGLE_1, SHEAR_ANGLE_2, PRE_COMMAND, POST_COMMAND, RB_NUMBER' @@ -105,12 +106,14 @@ def pnl_settings_on_write_clicked(self): filepath = filedialog.asksaveasfilename(defaultextension=filetypes[0][1], filetypes=filetypes) if filepath: scriptname = filepath.split('/')[-1].replace(" ", "_").rsplit('.', 1)[0] - script = self.inst_info.script_generator.generate_script(self.data.runs, self.settings.experiment, scriptname) + script_generator= ScriptGenerator() + script = script_generator.generate_script(self.data.runs, self.settings.experiment, scriptname) with open(filepath, 'w') as file: file.write(script) def pnl_settings_on_preview_clicked(self): - script = self.inst_info.script_generator.generate_script(self.data.runs, self.settings.experiment, "my_script") + script_generator= ScriptGenerator() + script = script_generator.generate_script(self.data.runs, self.settings.experiment, "my_script") print(script) preview_dialog = PreviewDialog(self, script) preview_dialog.show() @@ -149,19 +152,9 @@ def mnu_save_csv_click(self): messagebox.showerror("SANS Script - Error", "Could not save as CSV") def on_close(self): - if self.instrument.is_larmor: - last_instrument = "LARMOR" - elif self.instrument.is_loq: - last_instrument = "LOQ" - elif self.instrument.is_sans2d: - last_instrument = "SANS2D" - elif self.instrument.is_zoom: - last_instrument = "ZOOM" - else: - last_instrument = "LARMOR" # Save last_instrument to settings settings = Settings() - settings.last_instrument(last_instrument) + settings.last_instrument(self.instrument_var) self.destroy() diff --git a/Models/InstrumentInfo.py b/Models/InstrumentInfo.py index a0928d9..53d55ce 100644 --- a/Models/InstrumentInfo.py +++ b/Models/InstrumentInfo.py @@ -2,6 +2,7 @@ from tkinter import StringVar, BooleanVar from typing import List + class InstrumentInfo: property_changed = [] static_property_changed = [] @@ -68,14 +69,17 @@ def collection_mode_enabled(self, value): self._collection_mode_enabled.set(value) self.on_static_property_changed("collection_mode_enabled") +''' @property def script_generator(self): + self._script_generator = GeniePythonScriptGenerator() return self._script_generator @script_generator.setter def script_generator(self, value): self._script_generator = value ''' +''' class ScriptGenerator: @staticmethod def generate_script(runs, experiment, scriptname): diff --git a/Scripting/ScriptGenerator.py b/Scripting/ScriptGenerator.py index fcee1a5..9f7abe9 100644 --- a/Scripting/ScriptGenerator.py +++ b/Scripting/ScriptGenerator.py @@ -5,12 +5,9 @@ from Models.A2Setting import A2Setting class ScriptGenerator: - def __init__(self, is_larmor, is_loq, is_zoom, is_sans2d): + def __init__(self, instrument): self.indent = " " - self.is_larmor = is_larmor - self.is_loq = is_loq - self.is_zoom = is_zoom - self.is_sans2d = is_sans2d + self.is_instrument = instrument def generate_script(self, runs, settings, scriptname): script = f"# Script created by SANSScript at {datetime.now()}\n\n" @@ -28,13 +25,13 @@ def generate_header(self, scriptname): script += "from genie_python import genie as g, BLOCK_NAMES as b\n" script += "sys.path.append(r\"c:\\instrument\\scripts\")\n" - if self.is_larmor: + if self.is_instrument == "LARMOR": script += "from instrument.larmor import * # pylint: disable=wildcard-import, unused-wildcard-import\n" - if self.is_loq: + if self.is_instrument == "LOQ": script += "from instrument.loq import * # pylint: disable=wildcard-import, unused-wildcard-import\n" - if self.is_zoom: + if self.is_instrument == "ZOOM": script += "from instrument.zoom import * # pylint: disable=wildcard-import, unused-wildcard-import\n" - if self.is_sans2d: + if self.is_instrument == "SANS2D": script += "from instrument.sans2d import * # pylint: disable=wildcard-import, unused-wildcard-import\n" script += f"\ndef {scriptname}():\n" @@ -49,7 +46,7 @@ def generate_setup(self, settings): def check_positions(self, runs): script = "" - if self.is_larmor: + if self.is_instrument == "LARMOR": position_list = ",".join([exp.position for exp in runs if exp.position]) if position_list: script += self.get_indent_string(1) + f"if check_move_pos(['{position_list}']) != True:\n" @@ -119,9 +116,9 @@ def do_trans_run(self, script, exp, indent, settings): waitfor = "seconds" elif exp.trans_wait == "Minutes": waitfor = "minutes" - if self.is_larmor or self.is_zoom: + if self.is_instrument == "LARMOR" or self.is_instrument == "ZOOM": script += self.get_indent_string(indent) + f"do_trans(title='{fulltitle}', pos='{exp.position}', {waitfor}={exp.trans}, thickness={exp.thickness})\n" - elif self.is_loq: + elif self.is_instrument == "LOQ": script += self.get_indent_string(indent) + f"do_trans(title='{fulltitle}', pos='{exp.position}', {waitfor}={exp.trans}, thickness={exp.thickness}, aperture='small')\n" else: script += self.get_indent_string(indent) + f"do_trans(title='{fulltitle}', pos='{exp.position}', {waitfor}={exp.trans}, thickness={exp.thickness}, dae='transmission', aperture='{self.aperture_string(settings.a2_setting_trans)}')\n" @@ -147,7 +144,7 @@ def do_sans_run(self, script, exp, indent, settings): waitfor = "seconds" elif exp.sans_wait == "Minutes": waitfor = "minutes" - if self.is_larmor or self.is_zoom: + if self.is_instrument == "LARMOR" or self.is_instrument == "ZOOM": script += self.get_indent_string(indent) + f"do_sans(title='{fulltitle}', pos='{exp.position}', {waitfor}={exp.sans}, thickness={exp.thickness}, dae='{rtype}')\n" else: script += self.get_indent_string(indent) + f"do_sans(title='{fulltitle}', pos='{exp.position}', {waitfor}={exp.sans}, thickness={exp.thickness}, dae='{rtype}', aperture='{self.aperture_string(settings.a2_setting_sans)}')\n" @@ -241,7 +238,8 @@ def get_minutes(wait, length, count_rate): runs = [Experiment(position="pos1", trans=10, sans=20, trans_wait="Minutes", sans_wait="Minutes", sample="Sample1", thickness=1.0)] settings = ExperimentSettings(sample_width="10", sample_height="10", sample_geometry="Flat", order="AllTrans", num_trans=1, num_sans=1, collection_mode="Events", a2_setting_trans="Large", a2_setting_sans="Large") estimation = Estimation(count_rate=100, move_time=10) - generator = ScriptGenerator(is_larmor=True, is_loq=False, is_zoom=False, is_sans2d=False) + is_instrument = "LARMOR" + generator = ScriptGenerator(is_instrument) script = generator.generate_script(runs, settings, "my_script") print(script) estimated_time = generator.estimate_run_time(runs, settings, estimation) diff --git a/ViewModels/InstrumentVM.py b/ViewModels/InstrumentVM.py index b74bd98..f88b9e5 100644 --- a/ViewModels/InstrumentVM.py +++ b/ViewModels/InstrumentVM.py @@ -22,10 +22,7 @@ def __init__(self, settings, estimation): self.larmor_aperture_sizes = [A2Setting.LARMOR_LARGE,A2Setting.LARMOR_MEDIUM,A2Setting.LARMOR_SMALL] self.zoom_aperture_sizes = [A2Setting.LARGE, A2Setting.MEDIUM, A2Setting.SMALL] - self.is_larmor = BooleanVar(value=True) - self.is_loq = BooleanVar(value=False) - self.is_sans2d = BooleanVar(value=False) - self.is_zoom = BooleanVar(value=False) + self.is_instrument = StringVar(value="LARMOR") ''' self.is_larmor = False self.is_loq = False @@ -33,30 +30,25 @@ def __init__(self, settings, estimation): self.is_zoom = False ''' - self.is_larmor.trace_add("write", self.update_instrument) - self.is_loq.trace_add("write", self.update_instrument) - self.is_sans2d.trace_add("write", self.update_instrument) - self.is_zoom.trace_add("write", self.update_instrument) + self.is_instrument.trace_add("write", self.update_instrument) + self.is_instrument.trace_add("write", self.on_property_changed) self.update_instrument() def update_instrument(self, *args): - if self.is_larmor.get(): - self.set_instrument(True, False, False, False, "A1, S1 Setting:", self.larmor_aperture_sizes, 40) - elif self.is_loq.get(): - self.set_instrument(False, True, False, False, "A2 Setting:", self.loq_aperture_sizes, 170) - elif self.is_sans2d.get(): - self.set_instrument(False, False, True, False, "A6 Setting:", self.sans2d_aperture_sizes, 40) - elif self.is_zoom.get(): - self.set_instrument(False, False, False, True, "A6 Setting:", self.zoom_aperture_sizes, 40) - - def set_instrument(self, larmor, loq, sans2d, zoom, a2_label, a2_choices, count_rate): - self.is_larmor.set(larmor) - self.is_loq.set(loq) - self.is_sans2d.set(sans2d) - self.is_zoom.set(zoom) - - InstrumentInfo.script_generator = ScriptGenerator(larmor, loq, zoom, sans2d) + if self.is_instrument.get() == "LARMOR": + self.set_instrument(self.is_instrument.get(), "A1, S1 Setting:", self.larmor_aperture_sizes, 40) + elif self.is_instrument.get() == "LOQ": + self.set_instrument(self.is_instrument.get(), "A2 Setting:", self.loq_aperture_sizes, 170) + elif self.is_instrument.get() == "SANS2D": + self.set_instrument(self.is_instrument.get(), "A6 Setting:", self.sans2d_aperture_sizes, 40) + elif self.is_instrument.get() == "ZOOM": + self.set_instrument(self.is_instrument.get(), "A6 Setting:", self.zoom_aperture_sizes, 40) + + def set_instrument(self, instrument, a2_label, a2_choices, count_rate): + self.is_instrument.set(instrument) + + InstrumentInfo.script_generator = ScriptGenerator(instrument) InstrumentInfo.a2_label = a2_label InstrumentInfo.a2_choices = a2_choices @@ -67,7 +59,8 @@ def set_instrument(self, larmor, loq, sans2d, zoom, a2_label, a2_choices, count_ self.settings.a2_setting_sans = Settings.a2_setting_sans self.settings.a2_setting_trans = Settings.a2_setting_trans - + def on_property_changed(self, *args): + print(f"Property {args[0]} changed to "+ self.is_instrument.get()) if __name__ == "__main__": @@ -76,12 +69,5 @@ def set_instrument(self, larmor, loq, sans2d, zoom, a2_label, a2_choices, count_ estimation = Estimation() vm = InstrumentVM(settings, estimation) - def on_property_changed(name): - print(f"Property {name} changed") - - vm.is_larmor.trace_add("write", lambda *args: on_property_changed("is_larmor")) - vm.is_loq.trace_add("write", lambda *args: on_property_changed("is_loq")) - vm.is_sans2d.trace_add("write", lambda *args: on_property_changed("is_sans2d")) - vm.is_zoom.trace_add("write", lambda *args: on_property_changed("is_zoom")) - + vm.is_instrument = StringVar(value="loq") root.mainloop() \ No newline at end of file From 25cdfc86c172aff884a39e7000333c04f557caa7 Mon Sep 17 00:00:00 2001 From: Dirk Honecker Date: Fri, 27 Dec 2024 01:46:03 +0000 Subject: [PATCH 56/88] Fixing UnitTesting ScriptGenerator --- Models/Experiment.py | 42 +++++++++++++++---------------- Models/ExperimentSettings.py | 48 ++++++++++++++++++++++++++---------- Scripting/ScriptGenerator.py | 37 ++++++++++++++++----------- ViewModels/InstrumentVM.py | 2 +- 4 files changed, 79 insertions(+), 50 deletions(-) diff --git a/Models/Experiment.py b/Models/Experiment.py index 8aaefa0..5b9292f 100644 --- a/Models/Experiment.py +++ b/Models/Experiment.py @@ -2,26 +2,26 @@ class Experiment: - def __init__(self): - self._position = tk.StringVar(value='') - self._trans = tk.DoubleVar(value=0) - self._trans_wait = tk.StringVar(value='') - self._sans = tk.DoubleVar(value=0) - self._sans_wait = tk.StringVar(value='') - self._period = tk.StringVar(value='') - self._background = tk.StringVar(value='') - self._sample = tk.StringVar(value='') - self._thickness = tk.DoubleVar(value=0) - self._temperature1 = tk.StringVar(value='') - self._temperature2 = tk.StringVar(value='') - self._field = tk.StringVar(value='') - self._shear_rate1 = tk.StringVar(value='') - self._shear_rate2 = tk.StringVar(value='') - self._shear_angle1 = tk.StringVar(value='') - self._shear_angle2 = tk.StringVar(value='') - self._pre_command = tk.StringVar(value='') - self._post_command = tk.StringVar(value='') - self._rb_number = tk.StringVar(value='') + def __init__(self, position='', trans=0, trans_wait='', sans=0, sans_wait='', period='', background='', sample='', thickness=0, temperature1='', temperature2='', field='', shear_rate1='', shear_rate2='', shear_angle1='', shear_angle2='', pre_command='', post_command='', rb_number=''): + self._position = tk.StringVar(value=position) + self._trans = tk.DoubleVar(value=trans) + self._trans_wait = tk.StringVar(value=trans_wait) + self._sans = tk.DoubleVar(value=sans) + self._sans_wait = tk.StringVar(value=sans_wait) + self._period = tk.StringVar(value=period) + self._background = tk.StringVar(value=background) + self._sample = tk.StringVar(value=sample) + self._thickness = tk.DoubleVar(value=thickness) + self._temperature1 = tk.StringVar(value=temperature1) + self._temperature2 = tk.StringVar(value=temperature2) + self._field = tk.StringVar(value=field) + self._shear_rate1 = tk.StringVar(value=shear_rate1) + self._shear_rate2 = tk.StringVar(value=shear_rate2) + self._shear_angle1 = tk.StringVar(value=shear_angle1) + self._shear_angle2 = tk.StringVar(value=shear_angle2) + self._pre_command = tk.StringVar(value=pre_command) + self._post_command = tk.StringVar(value=post_command) + self._rb_number = tk.StringVar(value=rb_number) self.property_changed = [] self._position.trace_add("write", self.on_property_changed) @@ -204,7 +204,7 @@ def rb_number(self, value): if __name__ == "__main__": def on_property_changed(name): print(f"Property {name} changed") - + root = tk.Tk() experiment = Experiment() experiment.property_changed.append(on_property_changed) diff --git a/Models/ExperimentSettings.py b/Models/ExperimentSettings.py index 1a65435..42dbfe4 100644 --- a/Models/ExperimentSettings.py +++ b/Models/ExperimentSettings.py @@ -31,6 +31,30 @@ def __init__(self): self._sample_width.trace_add("write", self.on_property_changed) self._collection_mode.trace_add("write", self.on_property_changed) + def __call__(self, order=None, num_sans=None, num_trans=None, loop_per_run=None, a2_setting_sans=None, a2_setting_trans=None, sample_geometry=None, sample_height=None, sample_width=None, collection_mode=None, is_valid=None): + if order is not None: + self.order = order + if num_sans is not None: + self.num_sans = num_sans + if num_trans is not None: + self.num_trans = num_trans + if loop_per_run is not None: + self.loop_per_run = loop_per_run + if a2_setting_sans is not None: + self.a2_setting_sans = a2_setting_sans + if a2_setting_trans is not None: + self.a2_setting_trans = a2_setting_trans + if sample_geometry is not None: + self.sample_geometry = sample_geometry + if sample_height is not None: + self.sample_height = sample_height + if sample_width is not None: + self.sample_width = sample_width + if collection_mode is not None: + self.collection_mode = collection_mode + if is_valid is not None: + self.is_valid = is_valid + def on_property_changed(self, *args): for callback in self.property_changed: callback(args[0]) @@ -38,19 +62,17 @@ def on_property_changed(self, *args): def save(self): settings = Settings() - settings.run_order(str(self._order.get())) - settings.num_sans(str(self._num_sans.get())) - settings.num_sans(str(self._num_trans.get())) - settings.loop_per_run(str(self._loop_per_run.get())) - print(self._a2_setting_sans.get()) - settings.a2_setting_sans(self._a2_setting_sans.get()) - settings.a2_setting_trans(self._a2_setting_trans.get()) - settings.geometry(self._sample_geometry.get()) - settings.sample_height(str(self._sample_height.get())) - settings.sample_width(str(self._sample_width.get())) - settings.collection_mode(self._collection_mode.get()) - with open(self.config_file, 'w') as configfile: - self.config.write(configfile) + settings.run_order = self.order + settings.num_sans = self.num_sans + settings.num_trans = self.num_trans + settings.loop_per_run = self.loop_per_run + settings.a2_setting_sans = self.a2_setting_sans + settings.a2_setting_trans = self.a2_setting_trans + settings.geometry = self.sample_geometry + settings.sample_height = self.sample_height + settings.sample_width = self.sample_width + settings.collection_mode = self.collection_mode + settings.save() @property def order(self): diff --git a/Scripting/ScriptGenerator.py b/Scripting/ScriptGenerator.py index 9f7abe9..ebe6db8 100644 --- a/Scripting/ScriptGenerator.py +++ b/Scripting/ScriptGenerator.py @@ -1,4 +1,6 @@ -from datetime import datetime, timedelta +import tkinter as tk + +from datetime import datetime, timedelta from Models.Estimation import Estimation from Models.Experiment import Experiment from Models.ExperimentSettings import ExperimentSettings @@ -7,7 +9,8 @@ class ScriptGenerator: def __init__(self, instrument): self.indent = " " - self.is_instrument = instrument + self.is_instrument = tk.StringVar() # Initialize the attribute + self.is_instrument.set(instrument) def generate_script(self, runs, settings, scriptname): script = f"# Script created by SANSScript at {datetime.now()}\n\n" @@ -25,13 +28,13 @@ def generate_header(self, scriptname): script += "from genie_python import genie as g, BLOCK_NAMES as b\n" script += "sys.path.append(r\"c:\\instrument\\scripts\")\n" - if self.is_instrument == "LARMOR": + if self.is_instrument.get() == "LARMOR": script += "from instrument.larmor import * # pylint: disable=wildcard-import, unused-wildcard-import\n" - if self.is_instrument == "LOQ": + if self.is_instrument.get() == "LOQ": script += "from instrument.loq import * # pylint: disable=wildcard-import, unused-wildcard-import\n" - if self.is_instrument == "ZOOM": + if self.is_instrument.get() == "ZOOM": script += "from instrument.zoom import * # pylint: disable=wildcard-import, unused-wildcard-import\n" - if self.is_instrument == "SANS2D": + if self.is_instrument.get() == "SANS2D": script += "from instrument.sans2d import * # pylint: disable=wildcard-import, unused-wildcard-import\n" script += f"\ndef {scriptname}():\n" @@ -46,7 +49,7 @@ def generate_setup(self, settings): def check_positions(self, runs): script = "" - if self.is_instrument == "LARMOR": + if self.is_instrument.get() == "LARMOR": position_list = ",".join([exp.position for exp in runs if exp.position]) if position_list: script += self.get_indent_string(1) + f"if check_move_pos(['{position_list}']) != True:\n" @@ -116,9 +119,9 @@ def do_trans_run(self, script, exp, indent, settings): waitfor = "seconds" elif exp.trans_wait == "Minutes": waitfor = "minutes" - if self.is_instrument == "LARMOR" or self.is_instrument == "ZOOM": + if self.is_instrument.get() == "LARMOR" or self.is_instrument.get() == "ZOOM": script += self.get_indent_string(indent) + f"do_trans(title='{fulltitle}', pos='{exp.position}', {waitfor}={exp.trans}, thickness={exp.thickness})\n" - elif self.is_instrument == "LOQ": + elif self.is_instrument.get() == "LOQ": script += self.get_indent_string(indent) + f"do_trans(title='{fulltitle}', pos='{exp.position}', {waitfor}={exp.trans}, thickness={exp.thickness}, aperture='small')\n" else: script += self.get_indent_string(indent) + f"do_trans(title='{fulltitle}', pos='{exp.position}', {waitfor}={exp.trans}, thickness={exp.thickness}, dae='transmission', aperture='{self.aperture_string(settings.a2_setting_trans)}')\n" @@ -144,7 +147,7 @@ def do_sans_run(self, script, exp, indent, settings): waitfor = "seconds" elif exp.sans_wait == "Minutes": waitfor = "minutes" - if self.is_instrument == "LARMOR" or self.is_instrument == "ZOOM": + if self.is_instrument.get() == "LARMOR" or self.is_instrument.get() == "ZOOM": script += self.get_indent_string(indent) + f"do_sans(title='{fulltitle}', pos='{exp.position}', {waitfor}={exp.sans}, thickness={exp.thickness}, dae='{rtype}')\n" else: script += self.get_indent_string(indent) + f"do_sans(title='{fulltitle}', pos='{exp.position}', {waitfor}={exp.sans}, thickness={exp.thickness}, dae='{rtype}', aperture='{self.aperture_string(settings.a2_setting_sans)}')\n" @@ -235,11 +238,15 @@ def get_minutes(wait, length, count_rate): if __name__ == "__main__": - runs = [Experiment(position="pos1", trans=10, sans=20, trans_wait="Minutes", sans_wait="Minutes", sample="Sample1", thickness=1.0)] - settings = ExperimentSettings(sample_width="10", sample_height="10", sample_geometry="Flat", order="AllTrans", num_trans=1, num_sans=1, collection_mode="Events", a2_setting_trans="Large", a2_setting_sans="Large") - estimation = Estimation(count_rate=100, move_time=10) - is_instrument = "LARMOR" - generator = ScriptGenerator(is_instrument) + root = tk.Tk() + experiment=Experiment() + runs = [experiment(position="pos1", trans=10, sans=20, trans_wait="Minutes", sans_wait="Minutes", sample="Sample1", thickness=1.0)] + experiment_settings =ExperimentSettings() + settings = experiment_settings(sample_width="10", sample_height="10", sample_geometry="Flat", order="AllTrans", num_trans=1, num_sans=1, collection_mode="Events", a2_setting_trans="Large", a2_setting_sans="Large") + estimate=Estimation() + estimation = estimate(count_rate=100, move_time=10) + instrument = "LARMOR" + generator = ScriptGenerator(instrument) script = generator.generate_script(runs, settings, "my_script") print(script) estimated_time = generator.estimate_run_time(runs, settings, estimation) diff --git a/ViewModels/InstrumentVM.py b/ViewModels/InstrumentVM.py index f88b9e5..4ff6867 100644 --- a/ViewModels/InstrumentVM.py +++ b/ViewModels/InstrumentVM.py @@ -69,5 +69,5 @@ def on_property_changed(self, *args): estimation = Estimation() vm = InstrumentVM(settings, estimation) - vm.is_instrument = StringVar(value="loq") + vm.is_instrument.set('loq') root.mainloop() \ No newline at end of file From d3bd49c7ba3ce5f412bf7f4ed4e9373f6e064cb3 Mon Sep 17 00:00:00 2001 From: Dirk Honecker Date: Fri, 27 Dec 2024 01:46:32 +0000 Subject: [PATCH 57/88] Fixing and UnitTesting ScriptGenerator --- Properties/Settings.ini | 14 ++--- TableTest.py | 111 ++++++++++++++++++++++++++++++++++++++++ TextFile2.py | 28 ++++++++++ 3 files changed, 146 insertions(+), 7 deletions(-) create mode 100644 TableTest.py create mode 100644 TextFile2.py diff --git a/Properties/Settings.ini b/Properties/Settings.ini index 9fff4e1..9d42e03 100644 --- a/Properties/Settings.ini +++ b/Properties/Settings.ini @@ -1,15 +1,15 @@ [DEFAULT] lastinstrument = LOQ -sampleheight = 11 -samplewidth = 11 -geometry = Flat Plate -runorder = Alternate - TRANS first +sampleheight = 10.0 +samplewidth = 10.0 +geometry = Flat +runorder = AllTrans numtrans = 1 numsans = 1 loopperrun = False -a2settingsans = -a2settingtrans = -collectionmode = 'Events' +a2settingsans = Large +a2settingtrans = Large +collectionmode = Events tableentries = 'POSITION, TRANS, TRANS_WAIT, SANS, SANS_WAIT, PERIOD, SAMPLE_IDENTIFICATION_STRING, BACKGROUND, THICKNESS, TEMPERATURE, TEMPERATURE2, FIELD, SHEAR_RATE_1, SHEAR_RATE_2, SHEAR_ANGLE_1, SHEAR_ANGLE_2, PRE_COMMAND, POST_COMMAND, RB_NUMBER' count_rate = 38 move_time = 120 diff --git a/TableTest.py b/TableTest.py new file mode 100644 index 0000000..6e6d202 --- /dev/null +++ b/TableTest.py @@ -0,0 +1,111 @@ + +''' +import tkinter as tk + +class SimpleTableInput(tk.Frame): + def __init__(self, parent, rows, columns): + tk.Frame.__init__(self, parent) + + self._entry = {} + self.rows = rows + self.columns = columns + + # register a command to use for validation + vcmd = (self.register(self._validate), "%P") + + # create the table of widgets + for row in range(self.rows): + for column in range(self.columns): + index = (row, column) + e = tk.Entry(self, validate="key", validatecommand=vcmd) + e.grid(row=row, column=column, stick="nsew") + self._entry[index] = e + # adjust column weights so they all expand equally + for column in range(self.columns): + self.grid_columnconfigure(column, weight=1) + # designate a final, empty row to fill up any extra space + self.grid_rowconfigure(rows, weight=1) + + def get(self): + '''Return a list of lists, containing the data in the table''' + result = [] + for row in range(self.rows): + current_row = [] + for column in range(self.columns): + index = (row, column) + current_row.append(self._entry[index].get()) + result.append(current_row) + return result + + def _validate(self, P): + '''Perform input validation. + + Allow only an empty value, or a value that can be converted to a float + ''' + if P.strip() == "": + return True + + try: + f = float(P) + except ValueError: + self.bell() + return False + return True + +class Example(tk.Frame): + def __init__(self, parent): + tk.Frame.__init__(self, parent) + self.table = SimpleTableInput(self, 3, 4) + self.submit = tk.Button(self, text="Submit", command=self.on_submit) + self.table.pack(side="top", fill="both", expand=True) + self.submit.pack(side="bottom") + + def on_submit(self): + print(self.table.get()) + + +root = tk.Tk() +Example(root).pack(side="top", fill="both", expand=True) +root.mainloop() +''' + + +''' +move to next column and row with enter and tab +import tkinter as tk + +class Example(): + def __init__(self): + self.root = tk.Tk() + self.table = tk.Frame(self.root) + self.table.pack(fill="both", expand=True) + + self.rows = [] + for row in range(10): + row_entries = [] + self.rows.append(row_entries) + for column in range(4): + entry = tk.Entry(self.table) + entry.grid(row=row, column=column) + row_entries.append(entry) + + entry.bind("", self.handle_enter) + + def handle_enter(self, event): + # get the row and column of the entry that got the event + entry = event.widget + row = int(entry.grid_info()['row']) + column = int(entry.grid_info()['column']) + + # compute the new row; either the next row or circle + # back around to the first row + new_row = row+1 if row+1 < len(self.rows) else 0 + + # get the entry for the new row, and set focus to it + entry = self.rows[new_row][column] + entry.focus_set() + +example = Example() +tk.mainloop() +''' + diff --git a/TextFile2.py b/TextFile2.py new file mode 100644 index 0000000..d64e6a0 --- /dev/null +++ b/TextFile2.py @@ -0,0 +1,28 @@ +import tkinter as tk +from tkinter import ttk, filedialog, messagebox +from tkinter.scrolledtext import ScrolledText +import pandas as pd +from Models.DataCollection import DataCollection +from ViewModels.DataGridVM import DataGridVM +from ViewModels.SettingsPanelVM import SettingsPanelVM +from ViewModels.InstrumentVM import InstrumentVM +from Models.InstrumentInfo import InstrumentInfo +from Models.Estimation import Estimation +from Models.ExperimentSettings import ExperimentSettings +from IO.ExcelIO import ExcelIO +from IO.CsvExporter import CsvExporter +from Dialogs.PreviewDialog import PreviewDialog +from SettingsPanel import SettingsPanel +from EstimationPanel import EstimationPanel +from TableWidget import TableWidget +from Properties.Settings import Settings +from Scripting.ScriptGenerator import ScriptGenerator + +from datetime import datetime, timedelta +from Models.Estimation import Estimation +from Models.Experiment import Experiment +from Models.ExperimentSettings import ExperimentSettings +from Models.A2Setting import A2Setting + + + From 99d52c10c18665e94464e0ee06f257a5107cae13 Mon Sep 17 00:00:00 2001 From: Dirk Honecker Date: Mon, 30 Dec 2024 04:20:14 +0000 Subject: [PATCH 58/88] Complete GUI Table add instrument specific choices and DAE mode --- App.config | 48 -------------- EstimationPanel.py | 2 +- IO/CsvExporter.py | 4 +- IO/ExcelIO.py | 5 +- MainWindow.py | 37 ++++++----- Models/A2Setting.py | 42 ++++++++++-- Models/DataCollection.py | 35 ++++++++-- Models/Estimation.py | 8 +-- Models/Experiment.py | 2 +- Models/ExperimentSettings.py | 106 +++++++++++++++++++++--------- Models/InstrumentInfo.py | 55 +++++++++------- Properties/Settings.ini | 16 ++--- Properties/Settings.py | 61 ++++++++--------- Scripting/ScriptGenerator.py | 13 ++-- SettingsPanel.py | 119 ++++++++++++++++++++++++++-------- TableDataConstructor.py | 16 ++--- TableWidget.py | 76 +++++++++++----------- ViewModels/InstrumentVM.py | 61 ++++------------- ViewModels/SettingsPanelVM.py | 13 ++-- app_config.ini | 15 ----- 20 files changed, 408 insertions(+), 326 deletions(-) delete mode 100644 App.config delete mode 100644 app_config.ini diff --git a/App.config b/App.config deleted file mode 100644 index 2f23160..0000000 --- a/App.config +++ /dev/null @@ -1,48 +0,0 @@ - - - - -
- - - - - - - - - LARMOR - - - 11 - - - 11 - - - Flat Plate - - - Alternate - TRANS first - - - 1 - - - 1 - - - False - - - Large = 25, 16 mm - - - Large = 25, 16 mm - - - Events - - - - diff --git a/EstimationPanel.py b/EstimationPanel.py index 064087e..e74f8e8 100644 --- a/EstimationPanel.py +++ b/EstimationPanel.py @@ -45,7 +45,7 @@ def create_widgets(self): ttk.Label(grid_frame, text="Est. script time:").grid(row=2, column=0, sticky=tk.E) script_time_frame = ttk.Frame(grid_frame) script_time_frame.grid(row=2, column=1, columnspan=2) - self.txt_estimate = ttk.Entry(script_time_frame, width=15, textvariable=self.estimate.script_time, state='readonly') + self.txt_estimate = ttk.Entry(script_time_frame, width=15, textvariable=self.estimate.script_time, state="readonly") self.txt_estimate.grid(row=0, column=0, padx=5, pady=5) # Calculate button diff --git a/IO/CsvExporter.py b/IO/CsvExporter.py index 7a759ef..4c86197 100644 --- a/IO/CsvExporter.py +++ b/IO/CsvExporter.py @@ -4,7 +4,7 @@ class CsvExporter: @staticmethod def export(file, runs): - with open(file, mode='w', newline='') as csvfile: + with open(file, mode="w", newline="") as csvfile: writer = csv.writer(csvfile) for exp in runs: row = [ @@ -36,4 +36,4 @@ def export(file, runs): runs = [ Experiment(position="pos1", trans=10, trans_wait="Minutes", sans=20, sans_wait="Minutes", period="1", sample="Sample1", background="Background1", thickness="1.0", temperature1="25", temperature2="30", field="1.5T", shear_rate1="100", shear_rate2="200", shear_angle1="45", shear_angle2="90", pre_command="Pre Command", post_command="Post Command", rb_number="RB12345") ] - CsvExporter.export('output.csv', runs) \ No newline at end of file + CsvExporter.export("output.csv", runs) \ No newline at end of file diff --git a/IO/ExcelIO.py b/IO/ExcelIO.py index 8b33798..89b9a40 100644 --- a/IO/ExcelIO.py +++ b/IO/ExcelIO.py @@ -17,6 +17,7 @@ def __init__(self): def open_excel_workbook(self, filename): try: self.dataset = pd.read_excel(filename, sheet_name=None) + except Exception as ex: messagebox.showerror("SANS Script - Error", str(ex)) raise IOError("Could not open Excel spreadsheet") @@ -77,8 +78,8 @@ def export_data_to_excel(self, runs, filename): Experiment(position="pos1", trans=10, trans_wait="Minutes", sans=20, sans_wait="Minutes", period="1", sample="Sample1", background="Background1", thickness="1.0", temperature1="25", temperature2="30", field="1.5T", shear_rate1="100", shear_rate2="200", shear_angle1="45", shear_angle2="90", pre_command="Pre Command", post_command="Post Command", rb_number="RB12345") ] excel_io = ExcelIO() - excel_io.export_data_to_excel(runs, 'output.xlsx') - excel_io.open_excel_workbook('output.xlsx') + excel_io.export_data_to_excel(runs, "output.xlsx") + excel_io.open_excel_workbook("output.xlsx") experiments = excel_io.extract_experiments() for exp in experiments: print(exp.__dict__) \ No newline at end of file diff --git a/MainWindow.py b/MainWindow.py index e3af20c..8af2463 100644 --- a/MainWindow.py +++ b/MainWindow.py @@ -1,11 +1,10 @@ import tkinter as tk from tkinter import ttk, filedialog, messagebox from tkinter.scrolledtext import ScrolledText -import pandas as pd from Models.DataCollection import DataCollection from ViewModels.DataGridVM import DataGridVM -from ViewModels.SettingsPanelVM import SettingsPanelVM -from ViewModels.InstrumentVM import InstrumentVM +#from ViewModels.SettingsPanelVM import SettingsPanelVM +#from ViewModels.InstrumentVM import InstrumentVM from Models.InstrumentInfo import InstrumentInfo from Models.Estimation import Estimation from IO.ExcelIO import ExcelIO @@ -16,8 +15,9 @@ from TableWidget import TableWidget from Properties.Settings import Settings from Scripting.ScriptGenerator import ScriptGenerator +from Models.ExperimentSettings import ExperimentSettings -TableEntries = 'POSITION, TRANS, TRANS_WAIT, SANS, SANS_WAIT, PERIOD, SAMPLE_IDENTIFICATION_STRING, BACKGROUND, THICKNESS, TEMPERATURE, TEMPERATURE2, FIELD, SHEAR_RATE_1, SHEAR_RATE_2, SHEAR_ANGLE_1, SHEAR_ANGLE_2, PRE_COMMAND, POST_COMMAND, RB_NUMBER' +TableEntries = "POSITION, TRANS, TRANS_WAIT, SANS, SANS_WAIT, PERIOD, SAMPLE_IDENTIFICATION_STRING, BACKGROUND, THICKNESS, TEMPERATURE, TEMPERATURE2, FIELD, SHEAR_RATE_1, SHEAR_RATE_2, SHEAR_ANGLE_1, SHEAR_ANGLE_2, PRE_COMMAND, POST_COMMAND, RB_NUMBER" class MainWindow(tk.Frame): @@ -29,9 +29,11 @@ def __init__(self,parent): self.data = DataGridVM() self.inst_info = InstrumentInfo() - self.settings = SettingsPanelVM(self.inst_info) + self.settings = SettingsPanel(self, inst_info=self.inst_info) + self.settings.experiment = ExperimentSettings() + self.estimate = Estimation() - self.instrument = InstrumentVM(self.settings.experiment, self.estimate) + #self.instrument = InstrumentVM(self.settings.experiment, self.estimate) self.create_widgets() self.load_last_instrument() @@ -50,10 +52,10 @@ def create_widgets(self): #self.data_frame.grid(row=0, column=1, rowspan=4, sticky="nsew", padx=5, pady=5) self.data_frame.grid(row=0, column=1, sticky="nsew", padx=5, pady=5) - ''' + """ for col in self.data_grid["columns"]: self.data_grid.heading(col, text=col.replace("_", " ")) - ''' + """ # Settings Panel self.pnl_settings = SettingsPanel(self, self.settings) @@ -65,12 +67,13 @@ def create_widgets(self): self.pnl_estimate.grid(row=1, column=0, sticky="ew") self.pnl_estimate.on_calculate_clicked = self.pnl_estimate_on_calculate_clicked # Buttons - self.btn_create_new_table = ttk.Button(self, text="Create New Table", command=self.btn_create_new_table_click) - self.btn_create_new_table.grid(row=2, column=0, padx=5, pady=5, sticky="w") - + button_frame = ttk.Frame(self) + button_frame.grid(row=2, column=0, padx=5, pady=5, sticky="w") + self.btn_create_new_table = ttk.Button(button_frame, text="Create New Table", command=self.btn_create_new_table_click) + self.btn_create_new_table.grid(row=0, column=0, padx=5, pady=5) - self.btn_open_excel = ttk.Button(self, text="Open Excel", command=self.btn_open_excel_click) - self.btn_open_excel.grid(row=3, column=0, padx=5, pady=5, sticky="w") + self.btn_open_excel = ttk.Button(button_frame, text="Open Excel", command=self.btn_open_excel_click) + self.btn_open_excel.grid(row=0, column=1, padx=5, pady=5) self.mnu_main = tk.Menu(self.parent) @@ -90,10 +93,10 @@ def create_widgets(self): instrument_menu.add_radiobutton(label="LOQ", variable=self.instrument_var, value="LOQ") instrument_menu.add_radiobutton(label="SANS2D", variable=self.instrument_var, value="SANS2D") instrument_menu.add_radiobutton(label="ZOOM", variable=self.instrument_var, value="ZOOM") - ''' + """ def onFrameConfigure(self, event): self.canvas.configure(scrollregion=self.canvas.bbox("all")) - ''' + """ def load_last_instrument(self): settings = Settings() inst = settings.last_instrument # Replace with actual settings retrieval @@ -105,10 +108,10 @@ def pnl_settings_on_write_clicked(self): filetypes = [("genie_python Files", "*.py")] if self.instrument_var.get() in ["LARMOR", "LOQ", "ZOOM", "SANS2D"] else [("Open Genie Files", "*.gcl")] filepath = filedialog.asksaveasfilename(defaultextension=filetypes[0][1], filetypes=filetypes) if filepath: - scriptname = filepath.split('/')[-1].replace(" ", "_").rsplit('.', 1)[0] + scriptname = filepath.split("/")[-1].replace(" ", "_").rsplit(".", 1)[0] script_generator= ScriptGenerator() script = script_generator.generate_script(self.data.runs, self.settings.experiment, scriptname) - with open(filepath, 'w') as file: + with open(filepath, "w") as file: file.write(script) def pnl_settings_on_preview_clicked(self): diff --git a/Models/A2Setting.py b/Models/A2Setting.py index 51591d5..0921902 100644 --- a/Models/A2Setting.py +++ b/Models/A2Setting.py @@ -8,11 +8,41 @@ class A2Setting: LARMOR_MEDIUM = "Medium = 20, 14 mm" LARMOR_SMALL = "Small = 15, 10 mm" + + LARMOR_choices = [ + LARMOR_LARGE, + LARMOR_MEDIUM, + LARMOR_SMALL + ] + + LOQ_choices = [ + LARGE, MEDIUM, SMALL + ] + + SANS2D_choices = [ + X_LARGE, LARGE, MEDIUM, SMALL + ] + + ZOOM_choices = [ + LARGE, MEDIUM, SMALL + ] + + @staticmethod + def get_LARMOR_choices(): + return A2Setting.LARMOR_choices + + @staticmethod + def get_LOQ_choices(): + return A2Setting.LOQ_choices + + @staticmethod + def get_SANS2D_choices(): + return A2Setting.SANS2D_choices + + @staticmethod + def get_ZOOM_choices(): + return A2Setting.ZOOM_choices + # Example usage if __name__ == "__main__": - print(A2Setting.X_LARGE) - print(A2Setting.LARGE) - print(A2Setting.MEDIUM) - print(A2Setting.SMALL) - print(A2Setting.TRANS) - print(A2Setting.LARMOR) \ No newline at end of file + print(A2Setting.get_LOQ_choices()) \ No newline at end of file diff --git a/Models/DataCollection.py b/Models/DataCollection.py index 1d3ed8c..27b81b5 100644 --- a/Models/DataCollection.py +++ b/Models/DataCollection.py @@ -5,18 +5,41 @@ TRANSSANS_EVENTS = "TRANSSANS_Events" TRANSSANS_HISTOGRAM = "TRANSSANS_Histogram" - choices = [ + LOQ_choices = [ HISTOGRAM, TRANSSANS_HISTOGRAM, EVENTS, - TRANSSANS_EVENTS, - SESANS + TRANSSANS_EVENTS ] + SANS2D_choices = [ + EVENTS + ] + + ZOOM_choices = [ + EVENTS + ] + + LARMOR_choices = [ + EVENTS, SESANS + ] + + @staticmethod + def get_LARMOR_choices(): + return DataCollection.LARMOR_choices + + @staticmethod + def get_LOQ_choices(): + return DataCollection.LOQ_choices + + @staticmethod + def get_SANS2D_choices(): + return DataCollection.SANS2D_choices + @staticmethod - def get_choices(): - return DataCollection.choices + def get_ZOOM_choices(): + return DataCollection.ZOOM_choices # Example usage if __name__ == "__main__": - print(DataCollection.get_choices()) \ No newline at end of file + print(DataCollection.get_ZOOM_choices()) \ No newline at end of file diff --git a/Models/Estimation.py b/Models/Estimation.py index 9d296b5..53b1689 100644 --- a/Models/Estimation.py +++ b/Models/Estimation.py @@ -4,11 +4,11 @@ class Estimation: - def __init__(self, count_rate=0, move_time=0, script_time = 0): + def __init__(self, count_rate=40, move_time=120, script_time = 0): - self._count_rate = tk.DoubleVar(value=40) - self._move_time = tk.DoubleVar(value=120) - self._script_time = tk.DoubleVar(value=0) + self._count_rate = tk.DoubleVar(value=count_rate) + self._move_time = tk.DoubleVar(value=move_time) + self._script_time = tk.DoubleVar(value=script_time) self._is_valid = tk.BooleanVar(value=True) diff --git a/Models/Experiment.py b/Models/Experiment.py index 5b9292f..1d5dbac 100644 --- a/Models/Experiment.py +++ b/Models/Experiment.py @@ -2,7 +2,7 @@ class Experiment: - def __init__(self, position='', trans=0, trans_wait='', sans=0, sans_wait='', period='', background='', sample='', thickness=0, temperature1='', temperature2='', field='', shear_rate1='', shear_rate2='', shear_angle1='', shear_angle2='', pre_command='', post_command='', rb_number=''): + def __init__(self, position="", trans=0, trans_wait="", sans=0, sans_wait="", period="", background="", sample="", thickness=0, temperature1="", temperature2="", field="", shear_rate1="", shear_rate2="", shear_angle1="", shear_angle2="", pre_command="", post_command="", rb_number=""): self._position = tk.StringVar(value=position) self._trans = tk.DoubleVar(value=trans) self._trans_wait = tk.StringVar(value=trans_wait) diff --git a/Models/ExperimentSettings.py b/Models/ExperimentSettings.py index 42dbfe4..4c65eeb 100644 --- a/Models/ExperimentSettings.py +++ b/Models/ExperimentSettings.py @@ -1,25 +1,36 @@ import tkinter as tk from Properties.Settings import Settings +from Models.A2Setting import A2Setting +from Models.DataCollection import DataCollection +from Models.InstrumentInfo import InstrumentInfo +from Models.Estimation import Estimation +from Properties.Settings import Settings class ExperimentSettings: - def __init__(self): - settings = Settings() - - self._order = tk.StringVar(value=settings.run_order) - self._num_sans = tk.IntVar(value=settings.num_sans) - self._num_trans = tk.IntVar(value=settings.num_trans) - self._loop_per_run = tk.BooleanVar(value=settings.loop_per_run) - self._a2_setting_sans = tk.StringVar(value=settings.a2_setting_sans) - self._a2_setting_trans = tk.StringVar(value=settings.a2_setting_trans) - self._sample_geometry = tk.StringVar(value=settings.geometry) - self._sample_height = tk.DoubleVar(value=settings.sample_height) - self._sample_width = tk.DoubleVar(value=settings.sample_width) + def __init__(self, settings=None, estimation=None): + self.settings = settings if settings is not None else Settings() + self.estimation = estimation if estimation is not None else Estimation() + + + self._order = tk.StringVar(value=self.settings.run_order) + self._num_sans = tk.IntVar(value=self.settings.num_sans) + self._num_trans = tk.IntVar(value=self.settings.num_trans) + self._loop_per_run = tk.BooleanVar(value=self.settings.loop_per_run) + self._a2_setting_sans = tk.StringVar(value=self.settings.a2_setting_sans) + self._a2_setting_trans = tk.StringVar(value=self.settings.a2_setting_trans) + self._sample_geometry = tk.StringVar(value=self.settings.geometry) + self._sample_height = tk.DoubleVar(value=self.settings.sample_height) + self._sample_width = tk.DoubleVar(value=self.settings.sample_width) self._sample_width_enabled = tk.BooleanVar(value=True) - - self._collection_mode = tk.StringVar(value=settings.collection_mode) + + self._collection_mode = tk.StringVar(value=self.settings.collection_mode) self._is_valid = tk.BooleanVar(value=True) self.property_changed = [] + self.is_instrument = tk.StringVar(value=self.settings.last_instrument) + + self.is_instrument.trace_add("write", self.update_instrument) + self.is_instrument.trace_add("write", self.on_property_changed) self._order.trace_add("write", self.on_property_changed) self._num_sans.trace_add("write", self.on_property_changed) self._num_trans.trace_add("write", self.on_property_changed) @@ -31,6 +42,16 @@ def __init__(self): self._sample_width.trace_add("write", self.on_property_changed) self._collection_mode.trace_add("write", self.on_property_changed) + self.larmor_aperture_sizes=A2Setting.get_LARMOR_choices() + self.loq_aperture_sizes=A2Setting.get_LOQ_choices() + self.sans2d_aperture_sizes=A2Setting.get_SANS2D_choices() + self.zoom_aperture_sizes=A2Setting.get_ZOOM_choices() + + self.larmor_data_collection=DataCollection.get_LARMOR_choices() + self.loq_data_collection=DataCollection.get_LOQ_choices() + self.sans2d_data_collection=DataCollection.get_SANS2D_choices() + self.zoom_data_collection=DataCollection.get_ZOOM_choices() + def __call__(self, order=None, num_sans=None, num_trans=None, loop_per_run=None, a2_setting_sans=None, a2_setting_trans=None, sample_geometry=None, sample_height=None, sample_width=None, collection_mode=None, is_valid=None): if order is not None: self.order = order @@ -51,7 +72,7 @@ def __call__(self, order=None, num_sans=None, num_trans=None, loop_per_run=None, if sample_width is not None: self.sample_width = sample_width if collection_mode is not None: - self.collection_mode = collection_mode + self.collection_mode = collection_mode if is_valid is not None: self.is_valid = is_valid @@ -60,19 +81,44 @@ def on_property_changed(self, *args): callback(args[0]) self.save() + def update_instrument(self, *args): + if self.is_instrument.get() == "LARMOR": + self.set_instrument(self.is_instrument.get(), "A1, S1 Setting:", self.larmor_aperture_sizes,"Data Acquisition:",self.larmor_data_collection, 40) + elif self.is_instrument.get() == "LOQ": + self.set_instrument(self.is_instrument.get(), "A2 Setting:", self.loq_aperture_sizes,"Data Acquisition:",self.loq_data_collection, 170) + elif self.is_instrument.get() == "SANS2D": + self.set_instrument(self.is_instrument.get(), "A6 Setting:", self.sans2d_aperture_sizes,"Data Acquisition:",self.sans2d_data_collection, 40) + elif self.is_instrument.get() == "ZOOM": + self.set_instrument(self.is_instrument.get(), "A6 Setting:", self.zoom_aperture_sizes,"Data Acquisition:",self.zoom_data_collection, 40) + + def set_instrument(self, instrument, a2_label, a2_choices, data_collection_label, data_collection_choices, count_rate): + if not hasattr(self, 'settings') or self.settings is None: + self.settings = Settings() + + self.settings.is_instrument.set(instrument) + + self.settings.a2_label = a2_label + self.settings.a2_choices = a2_choices + self.settings.data_collection_label = data_collection_label + self.settings.data_collection_choices = data_collection_choices + self.settings.a2_trans_enabled = True + self.settings.collection_mode_enabled = True + self.estimation.count_rate = count_rate + + def save(self): - settings = Settings() - settings.run_order = self.order - settings.num_sans = self.num_sans - settings.num_trans = self.num_trans - settings.loop_per_run = self.loop_per_run - settings.a2_setting_sans = self.a2_setting_sans - settings.a2_setting_trans = self.a2_setting_trans - settings.geometry = self.sample_geometry - settings.sample_height = self.sample_height - settings.sample_width = self.sample_width - settings.collection_mode = self.collection_mode - settings.save() + + self.settings.run_order = self.order + self.settings.num_sans = self.num_sans + self.settings.num_trans = self.num_trans + self.settings.loop_per_run = self.loop_per_run + self.settings.a2_setting_sans = self.a2_setting_sans + self.settings.a2_setting_trans = self.a2_setting_trans + self.settings.geometry = self.sample_geometry + self.settings.sample_height = self.sample_height + self.settings.sample_width = self.sample_width + self.settings.collection_mode = self.collection_mode + self.settings.save() @property def order(self): @@ -129,7 +175,7 @@ def sample_geometry(self): @sample_geometry.setter def sample_geometry(self, value): self._sample_geometry.set(value) - self.sample_width_enabled = value != 'Disc' + self.sample_width_enabled = value != "Disc" @property def sample_height(self): @@ -158,7 +204,7 @@ def sample_width_enabled(self, value): self._sample_width_enabled.set(value) if not value: self.sample_width = self.sample_height - + @property def collection_mode(self): return self._collection_mode.get() @@ -166,7 +212,7 @@ def collection_mode(self): @collection_mode.setter def collection_mode(self, value): self._collection_mode.set(value) - + @property def is_valid(self): return self._is_valid.get() diff --git a/Models/InstrumentInfo.py b/Models/InstrumentInfo.py index 53d55ce..1521f2a 100644 --- a/Models/InstrumentInfo.py +++ b/Models/InstrumentInfo.py @@ -1,7 +1,7 @@ import tkinter as tk from tkinter import StringVar, BooleanVar from typing import List - +from Properties.Settings import Settings class InstrumentInfo: property_changed = [] @@ -9,21 +9,30 @@ class InstrumentInfo: script_generator = None a2_label = "" a2_choices = [] + data_collection_label = "" + data_collection_choices = [] a2_trans_enabled = False - collection_mode_enabled = False + collection_mode_enabled = True - def __init__(self): + def __init__(self,settings=None): + self.settings = settings if settings is not None else Settings() self._a2_label = StringVar(value="A2 Setting:") self._a2_choices = tk.Variable(value=["Large", "Medium", "Small"]) + self._data_collection_label = StringVar(value="Data acquisition:") + self._data_collection_choices = tk.Variable(value=["Event", "SESANS"]) self._a2_trans_enabled = BooleanVar(value=True) self._collection_mode_enabled = BooleanVar(value=False) self._script_generator = None + self._a2_label.trace_add("write", self.on_property_changed) self._a2_choices.trace_add("write", self.on_property_changed) + self._data_collection_label.trace_add("write", self.on_property_changed) + self._data_collection_choices.trace_add("write", self.on_property_changed) self._a2_trans_enabled.trace_add("write", self.on_property_changed) self._collection_mode_enabled.trace_add("write", self.on_property_changed) + def on_property_changed(self, *args): for callback in self.property_changed: callback(args[0]) @@ -51,6 +60,24 @@ def a2_choices(self, value: List[str]): self._a2_choices.set(value) self.on_static_property_changed("a2_choices") + @property + def data_collection_label(self): + return self._data_collection_label.get() + + @data_collection_label.setter + def data_collection_label(self, value): + self._data_collection_label.set(value) + self.on_static_property_changed("data_collection_label") + + @property + def data_collection_choices(self): + return self._data_collection_choices.get() + + @data_collection_choices.setter + def data_collection_choices(self, value: List[str]): + self._data_collection_choices.set(value) + self.on_static_property_changed("data_collection_choices") + @property def a2_trans_enabled(self): return self._a2_trans_enabled.get() @@ -69,28 +96,10 @@ def collection_mode_enabled(self, value): self._collection_mode_enabled.set(value) self.on_static_property_changed("collection_mode_enabled") -''' - @property - def script_generator(self): - self._script_generator = GeniePythonScriptGenerator() - return self._script_generator - - @script_generator.setter - def script_generator(self, value): - self._script_generator = value -''' -''' - class ScriptGenerator: - @staticmethod - def generate_script(runs, experiment, scriptname): - return f"# Script for {scriptname}\n# Runs: {runs}\n# Experiment: {experiment}" - - @staticmethod - def estimate_run_time(runs, experiment, estimate): - return len(runs) * 10 # Dummy implementation -''' + # Example usage if __name__ == "__main__": + root = tk.Tk() def on_property_changed(name): print(f"Property {name} changed") diff --git a/Properties/Settings.ini b/Properties/Settings.ini index 9d42e03..b5b631d 100644 --- a/Properties/Settings.ini +++ b/Properties/Settings.ini @@ -2,15 +2,15 @@ lastinstrument = LOQ sampleheight = 10.0 samplewidth = 10.0 -geometry = Flat -runorder = AllTrans +geometry = Flat Plate +runorder = All Trans First numtrans = 1 numsans = 1 loopperrun = False -a2settingsans = Large -a2settingtrans = Large -collectionmode = Events -tableentries = 'POSITION, TRANS, TRANS_WAIT, SANS, SANS_WAIT, PERIOD, SAMPLE_IDENTIFICATION_STRING, BACKGROUND, THICKNESS, TEMPERATURE, TEMPERATURE2, FIELD, SHEAR_RATE_1, SHEAR_RATE_2, SHEAR_ANGLE_1, SHEAR_ANGLE_2, PRE_COMMAND, POST_COMMAND, RB_NUMBER' -count_rate = 38 -move_time = 120 +a2settingsans = +a2settingtrans = +collectionmode = +tableentries = "POSITION, TRANS, TRANS_WAIT, SANS, SANS_WAIT, PERIOD, SAMPLE_IDENTIFICATION_STRING, BACKGROUND, THICKNESS, TEMPERATURE, TEMPERATURE2, FIELD, SHEAR_RATE_1, SHEAR_RATE_2, SHEAR_ANGLE_1, SHEAR_ANGLE_2, PRE_COMMAND, POST_COMMAND, RB_NUMBER" +countrate = 38 +movetime = 120 diff --git a/Properties/Settings.py b/Properties/Settings.py index 0b9b36f..2b2e2eb 100644 --- a/Properties/Settings.py +++ b/Properties/Settings.py @@ -2,136 +2,137 @@ import os class Settings: - def __init__(self, filename='Settings.ini'): + def __init__(self, filename="Settings.ini"): self.dict_settings = configparser.ConfigParser() directory=os.path.dirname(os.path.abspath(__file__)) - self.setting_ini_path = os.path.join(directory,filename) + self.setting_ini_path = os.path.join(directory,filename) + print(self.setting_ini_path) self.dict_settings.read(self.setting_ini_path) @property def last_instrument(self): - return self.dict_settings.get('DEFAULT','LastInstrument') + return self.dict_settings.get("DEFAULT","LastInstrument") @last_instrument.setter def last_instrument(self, value): - self.dict_settings.set('DEFAULT','LastInstrument', str(value)) + self.dict_settings.set("DEFAULT","LastInstrument", str(value)) self.save() @property def sample_height(self): - return self.dict_settings.get('DEFAULT','SampleHeight') + return self.dict_settings.get("DEFAULT","SampleHeight") @sample_height.setter def sample_height(self, value): - self.dict_settings.set('DEFAULT','SampleHeight', str(value)) + self.dict_settings.set("DEFAULT","SampleHeight", str(value)) self.save() @property def sample_width(self): - return self.dict_settings.get('DEFAULT','SampleWidth') + return self.dict_settings.get("DEFAULT","SampleWidth") @sample_width.setter def sample_width(self, value): - self.dict_settings.set('DEFAULT','SampleWidth', str(value)) + self.dict_settings.set("DEFAULT","SampleWidth", str(value)) self.save() @property def geometry(self): - return self.dict_settings.get('DEFAULT','Geometry') + return self.dict_settings.get("DEFAULT","Geometry") @geometry.setter def geometry(self, value): - self.dict_settings.set('DEFAULT','Geometry', str(value)) + self.dict_settings.set("DEFAULT","Geometry", str(value)) self.save() @property def run_order(self): - return self.dict_settings.get('DEFAULT','RunOrder') + return self.dict_settings.get("DEFAULT","RunOrder") @run_order.setter def run_order(self, value): - self.dict_settings.set('DEFAULT','RunOrder', str(value)) + self.dict_settings.set("DEFAULT","RunOrder", str(value)) self.save() @property def num_trans(self): - return self.dict_settings.get('DEFAULT','NumTrans') + return self.dict_settings.get("DEFAULT","NumTrans") @num_trans.setter def num_trans(self, value): - self.dict_settings.set('DEFAULT','NumTrans', str(value)) + self.dict_settings.set("DEFAULT","NumTrans", str(value)) self.save() @property def num_sans(self): - return self.dict_settings.get('DEFAULT','NumSans') + return self.dict_settings.get("DEFAULT","NumSans") @num_sans.setter def num_sans(self, value): - self.dict_settings.set('DEFAULT','NumSans', str(value)) + self.dict_settings.set("DEFAULT","NumSans", str(value)) self.save() @property def loop_per_run(self): - return self.dict_settings.get('DEFAULT','LoopPerRun') + return self.dict_settings.get("DEFAULT","LoopPerRun") @loop_per_run.setter def loop_per_run(self, value): - self.dict_settings.set('DEFAULT','LoopPerRun', str(value)) + self.dict_settings.set("DEFAULT","LoopPerRun", str(value)) self.save() @property def a2_setting_sans(self): - return self.dict_settings.get('DEFAULT','A2SettingSans') + return self.dict_settings.get("DEFAULT","A2SettingSans") @a2_setting_sans.setter def a2_setting_sans(self, value): - self.dict_settings.set('DEFAULT','A2SettingSans', str(value)) + self.dict_settings.set("DEFAULT","A2SettingSans", str(value)) self.save() @property def a2_setting_trans(self): - return self.dict_settings.get('DEFAULT','A2SettingTrans') + return self.dict_settings.get("DEFAULT","A2SettingTrans") @a2_setting_trans.setter def a2_setting_trans(self, value): - self.dict_settings.set('DEFAULT','A2SettingTrans', str(value)) + self.dict_settings.set("DEFAULT","A2SettingTrans", str(value)) self.save() @property def collection_mode(self): - return self.dict_settings.get('DEFAULT','CollectionMode') + return self.dict_settings.get("DEFAULT","CollectionMode") @collection_mode.setter def collection_mode(self, value): - self.dict_settings.set('DEFAULT','CollectionMode', str(value)) + self.dict_settings.set("DEFAULT","CollectionMode", str(value)) self.save() @property def count_rate(self): - return self.dict_settings.get('DEFAULT','count_rate') + return self.dict_settings.get("DEFAULT","CountRate") @count_rate.setter def count_rate(self, value): - self.dict_settings.set('DEFAULT','count_rate', str(value)) + self.dict_settings.set("DEFAULT","CountRate", str(value)) self.save() @property def move_time(self): - return self.dict_settings.get('DEFAULT','move_time') + return self.dict_settings.get("DEFAULT","MoveTime") @move_time.setter def move_time(self, value): - self.dict_settings.set('DEFAULT','move_time', str(value)) + self.dict_settings.set("DEFAULT","MoveTime", str(value)) self.save() def save(self): - with open(self.setting_ini_path, 'w') as configfile: + with open(self.setting_ini_path, "w") as configfile: self.dict_settings.write(configfile) # Example usage settings = Settings() print(settings.last_instrument) -settings.last_instrument = 'LOQ' +settings.last_instrument = "LOQ" print(settings.last_instrument) \ No newline at end of file diff --git a/Scripting/ScriptGenerator.py b/Scripting/ScriptGenerator.py index ebe6db8..597628c 100644 --- a/Scripting/ScriptGenerator.py +++ b/Scripting/ScriptGenerator.py @@ -5,6 +5,9 @@ from Models.Experiment import Experiment from Models.ExperimentSettings import ExperimentSettings from Models.A2Setting import A2Setting +from Models.RunOrder import RunOrder +from Models.DataCollection import DataCollection +from Models.A2Setting import A2Setting class ScriptGenerator: def __init__(self, instrument): @@ -62,7 +65,7 @@ def generate_runs(self, runs, settings): if not runs: return script - if settings.order == "AllTrans": + if settings.order == RunOrder.ALL_TRANS: script += self.get_indent_string(1) + f"for i in range({settings.num_trans}):\n" for exp in runs: if exp.trans: @@ -71,7 +74,7 @@ def generate_runs(self, runs, settings): for exp in runs: if exp.sans: script = self.do_sans_run(script, exp, 2, settings) - elif settings.order == "AllSans": + elif settings.order == RunOrder.ALL_SANS: script += self.get_indent_string(1) + f"for i in range({settings.num_sans}):\n" for exp in runs: if exp.sans: @@ -88,7 +91,7 @@ def generate_runs(self, runs, settings): script += self.get_indent_string(1) + "while True:\n" indent = 2 for exp in runs: - if settings.order == "TransFirst": + if settings.order == RunOrder.TRANS_FIRST: if exp.trans: script += self.get_indent_string(indent) + "if count < num_trans:\n" script = self.do_trans_run(script, exp, indent + 1, settings) @@ -111,8 +114,8 @@ def do_trans_run(self, script, exp, indent, settings): waitfor = "uamps" rtype = "histogram" fulltitle = f"{{{exp.sample}}}" - if settings.collection_mode == "Events": - rtype = "event" + if settings.collection_mode == DataCollection.EVENTS: + rtype = DataCollection.EVENTS if exp.trans_wait == "Frames": waitfor = "frames" elif exp.trans_wait == "Seconds": diff --git a/SettingsPanel.py b/SettingsPanel.py index 2199f0d..b141465 100644 --- a/SettingsPanel.py +++ b/SettingsPanel.py @@ -1,15 +1,30 @@ import tkinter as tk from tkinter import ttk -from ViewModels.SettingsPanelVM import SettingsPanelVM +#from ViewModels.SettingsPanelVM import SettingsPanelVM +#from ViewModels.InstrumentVM import InstrumentVM from Properties.Settings import Settings +from Models.RunOrder import RunOrder +from Models.Geometry import Geometry +from Models.ExperimentSettings import ExperimentSettings +from Models.InstrumentInfo import InstrumentInfo class SettingsPanel(ttk.Frame): - def __init__(self, parent, settings_vm=None): + #def __init__(self, parent, experiment=None, inst_info=None): + def __init__(self, parent,inst_info=None): super().__init__(parent) - - self.settings_vm = settings_vm if settings_vm is not None else SettingsPanelVM() + self.parent = parent + + self.inst_info = inst_info if inst_info is not None else InstrumentInfo() + #self.settings_vm = settings_vm if settings_vm is not None else SettingsPanelVM() + #self.instrument_vm = instrument_vm if instrument_vm is not None else InstrumentVM() self.on_preview_clicked = None self.on_write_clicked = None + self.property_changed = [] + #self.order_choices = RunOrder.get_choices() + #self.geometry_choices = Geometry.get_choices() + + #self._experiment = experiment if experiment is not None else ExperimentSettings() + #self._inst_info = inst_info if inst_info is not None else InstrumentInfo() self.create_widgets() @@ -19,7 +34,8 @@ def create_widgets(self): #self.write_button = ttk.Button(self, text="Write Script", command=self.on_write_clicked) #self.write_button.pack(side=tk.LEFT, padx=5, pady=5) - settings = Settings() + self.settings = Settings() + self.inst_info = InstrumentInfo() label = ttk.Label(self, text="Settings", font=("Helvetica", 16, "bold")) label.grid(row=0, column=0, columnspan=2, pady=10) @@ -29,21 +45,21 @@ def create_widgets(self): # Order ttk.Label(grid_frame, text="Order:").grid(row=0, column=0, sticky=tk.E) - print(self.settings_vm.order_choices) - print(settings.run_order) + print(self.order_choices) + print(self.settings.run_order) - self.com_order = ttk.Combobox(grid_frame, values=self.settings_vm.order_choices) - self.com_order['values']=self.settings_vm.order_choices + self.com_order = ttk.Combobox(grid_frame, values=self.order_choices) + self.com_order["values"]=self.order_choices self.com_order.grid(row=0, column=1, columnspan=2, padx=5, pady=5) - self.com_order.set(settings.run_order) # Set default value + self.com_order.set(self.settings.run_order) # Set default value ''' # Collection Mode - ttk.Label(grid_frame, text="Collection Mode:").grid(row=0, column=0, sticky=tk.E) + ttk.Label(grid_frame, text="Collection Mode:").grid(row=1, column=0, sticky=tk.E) print(self.settings_vm.collection_mode_choices) print(settings.collection_mode) self.collect_mode = ttk.Combobox(grid_frame, values=self.settings_vm.collection_mode_choices) - self.collect_mode['values']=self.settings_vm.collection_mode_choices + self.collect_mode["values"]=self.settings_vm.collection_mode_choices self.collect_mode.grid(row=1, column=1, columnspan=2, padx=5, pady=5) self.collect_mode.set(settings.collection_mode) # Set default value ''' @@ -53,7 +69,7 @@ def create_widgets(self): sans_frame = ttk.Frame(grid_frame) sans_frame.grid(row=1, column=1, columnspan=2) self.txt_times_sans = ttk.Entry(sans_frame, width=5) - self.txt_times_sans.insert(0, settings.num_sans) # Default value + self.txt_times_sans.insert(0, self.settings.num_sans) # Default value self.txt_times_sans.grid(row=0, column=0, padx=5, pady=5) ttk.Label(sans_frame, text="time(s)").grid(row=0, column=1) @@ -62,41 +78,61 @@ def create_widgets(self): trans_frame = ttk.Frame(grid_frame) trans_frame.grid(row=2, column=1, columnspan=2) self.txt_times_trans = ttk.Entry(trans_frame, width=5) - self.txt_times_trans.insert(0, settings.num_trans) # Default value + self.txt_times_trans.insert(0, self.settings.num_trans) # Default value self.txt_times_trans.grid(row=0, column=0, padx=5, pady=5) ttk.Label(trans_frame, text="time(s)").grid(row=0, column=1) # Loop over each run - self.chk_loop_run = ttk.Checkbutton(grid_frame, text="Loop over each run?") + self.loop_run_var = tk.BooleanVar(value=self.settings.loop_per_run) # Create BooleanVar and set default value + self.chk_loop_run = ttk.Checkbutton(grid_frame, text="Loop over each run?", variable=self.loop_run_var) self.chk_loop_run.grid(row=3, column=0, columnspan=3, pady=5) + # Collection Mode + tk.Label(grid_frame, text=self.inst_info.data_collection_label).grid(row=4, column=0, sticky=tk.E) + self.com_collection_mode = ttk.Combobox(grid_frame, values=self.inst_info.data_collection_choices) + self.com_collection_mode.grid(row=4, column=1, columnspan=2, padx=5, pady=5) + self.com_collection_mode.set(self.settings.collection_mode) # Set default value + + + # A2 SANS + tk.Label(grid_frame, text="SANS "+self.inst_info.a2_label).grid(row=5, column=0, sticky=tk.E) + self.com_a2_sans = ttk.Combobox(grid_frame, values=self.inst_info.a2_choices ) + self.com_a2_sans.grid(row=5, column=1, columnspan=2, padx=5, pady=5) + self.com_a2_sans.set(self.settings.a2_setting_sans) # Set default value + + # A2 TRANS + tk.Label(grid_frame, text="TRANS "+self.inst_info.a2_label).grid(row=6, column=0, sticky=tk.E) + self.com_a2_trans = ttk.Combobox(grid_frame, values=self.inst_info.a2_choices) + self.com_a2_trans.grid(row=6, column=1, columnspan=2, padx=5, pady=5) + self.com_a2_trans.set(self.settings.a2_setting_trans) # Set default value + # Sample Geometry - ttk.Label(grid_frame, text="Sample Geometry:").grid(row=6, column=0, sticky=tk.E) - self.com_sample_geo = ttk.Combobox(grid_frame, values=self.settings_vm.geometry_choices) - self.com_sample_geo.grid(row=6, column=1, columnspan=2, padx=5, pady=5) - self.com_sample_geo.set(settings.geometry) # Set default value + ttk.Label(grid_frame, text="Sample Geometry:").grid(row=7, column=0, sticky=tk.E) + self.com_sample_geo = ttk.Combobox(grid_frame, values=self.geometry_choices) + self.com_sample_geo.grid(row=7, column=1, columnspan=2, padx=5, pady=5) + self.com_sample_geo.set(self.settings.geometry) # Set default value # Sample Height - ttk.Label(grid_frame, text="Sample Height:").grid(row=7, column=0, sticky=tk.E) + ttk.Label(grid_frame, text="Sample Height:").grid(row=8, column=0, sticky=tk.E) sample_height_frame = ttk.Frame(grid_frame) - sample_height_frame.grid(row=7, column=1, columnspan=2) + sample_height_frame.grid(row=8, column=1, columnspan=2) self.txt_sample_height = ttk.Entry(sample_height_frame, width=5) - self.txt_sample_height.insert(0, settings.sample_height) # Default value + self.txt_sample_height.insert(0, self.settings.sample_height) # Default value self.txt_sample_height.grid(row=0, column=0, padx=5, pady=5) ttk.Label(sample_height_frame, text="mm").grid(row=0, column=1) # Sample Width - ttk.Label(grid_frame, text="Sample Width:").grid(row=8, column=0, sticky=tk.E) + ttk.Label(grid_frame, text="Sample Width:").grid(row=9, column=0, sticky=tk.E) sample_width_frame = ttk.Frame(grid_frame) - sample_width_frame.grid(row=8, column=1, columnspan=2) + sample_width_frame.grid(row=9, column=1, columnspan=2) self.txt_sample_width = ttk.Entry(sample_width_frame, width=5) - self.txt_sample_width.insert(0, settings.sample_width) # Default value + self.txt_sample_width.insert(0, self.settings.sample_width) # Default value self.txt_sample_width.grid(row=0, column=0, padx=5, pady=5) ttk.Label(sample_width_frame, text="mm").grid(row=0, column=1) # Buttons button_frame = ttk.Frame(self) - button_frame.grid(row=2, column=0, columnspan=2, pady=10) + button_frame.grid(row=10, column=0, columnspan=2, pady=10) self.btn_preview_script = ttk.Button(button_frame, text="Preview Script", command=self.btn_preview_script_click) self.btn_preview_script.grid(row=0, column=0, padx=5) self.btn_write_script = ttk.Button(button_frame, text="Write Script", command=self.btn_write_script_click) @@ -112,6 +148,37 @@ def btn_write_script_click(self): if self.on_write_clicked: self.on_write_clicked() + def on_property_changed(self, name): + for callback in self.property_changed: + callback(name) + + ''' + @property + def experiment(self): + return self._experiment + + @experiment.setter + def experiment(self, value): + self._experiment = value + self.on_property_changed("experiment") + + @property + def inst_info(self): + return self._inst_info + + @inst_info.setter + def inst_info(self, value): + self._inst_info = value + self.on_property_changed("inst_info") + ''' + @staticmethod + def order_choices(): + return RunOrder.get_choices() + + @staticmethod + def geometry_choices(): + return Geometry.get_choices() + if __name__ == "__main__": root = tk.Tk() diff --git a/TableDataConstructor.py b/TableDataConstructor.py index 313982a..9f04b99 100644 --- a/TableDataConstructor.py +++ b/TableDataConstructor.py @@ -1,17 +1,17 @@ class TableDataConstructor: def __init__(self, table_entries): - self.table_entries = table_entries.split(', ') + self.table_entries = table_entries.split(", ") def construct_empty_data(self): empty_data = { - 'rec1': {entry: '' for entry in self.table_entries} + "rec1": {entry: "" for entry in self.table_entries} } return empty_data def construct_data(self, data_list): data = {} for i, row in enumerate(data_list, start=1): - data[f'rec{i}'] = {entry: value for entry, value in zip(self.table_entries, row)} + data[f"rec{i}"] = {entry: value for entry, value in zip(self.table_entries, row)} return data def deconstruct_data(self, data_dict): @@ -22,20 +22,20 @@ def deconstruct_data(self, data_dict): return data_list # Example usage -TableEntries = 'POSITION, TRANS, TRANS_WAIT, SANS, SANS_WAIT, PERIOD, SAMPLE_IDENTIFICATION_STRING, BACKGROUND, THICKNESS, TEMPERATURE, TEMPERATURE2, FIELD, SHEAR_RATE_1, SHEAR_RATE_2, SHEAR_ANGLE_1, SHEAR_ANGLE_2, PRE_COMMAND, POST_COMMAND, RB_NUMBER' +TableEntries = "POSITION, TRANS, TRANS_WAIT, SANS, SANS_WAIT, PERIOD, SAMPLE_IDENTIFICATION_STRING, BACKGROUND, THICKNESS, TEMPERATURE, TEMPERATURE2, FIELD, SHEAR_RATE_1, SHEAR_RATE_2, SHEAR_ANGLE_1, SHEAR_ANGLE_2, PRE_COMMAND, POST_COMMAND, RB_NUMBER" constructor = TableDataConstructor(TableEntries) empty_data = constructor.construct_empty_data() print(empty_data) # Example usage -TableEntries = 'col1, col2, label, col3, col4, label2, col5, col6, label3' +TableEntries = "col1, col2, label, col3, col4, label2, col5, col6, label3" constructor = TableDataConstructor(TableEntries) # Construct data from a list of lists data_list = [ - [99.88, 108.79, 'rec1', 99.88, 108.79, 'rec1', 99.88, 108.79, 'rec1'], - [99.88, 108.79, 'rec2', 99.88, 108.79, 'rec2', 99.88, 108.79, 'rec2'], - [99.88, 108.79, 'rec3', 99.88, 108.79, 'rec3', 99.88, 108.79, 'rec3']] + [99.88, 108.79, "rec1", 99.88, 108.79, "rec1", 99.88, 108.79, "rec1"], + [99.88, 108.79, "rec2", 99.88, 108.79, "rec2", 99.88, 108.79, "rec2"], + [99.88, 108.79, "rec3", 99.88, 108.79, "rec3", 99.88, 108.79, "rec3"]] # Add more rows as needed data = constructor.construct_data(data_list) diff --git a/TableWidget.py b/TableWidget.py index ce17bcf..132309f 100644 --- a/TableWidget.py +++ b/TableWidget.py @@ -7,7 +7,7 @@ from collections import OrderedDict from TableDataConstructor import TableDataConstructor -TableEntries = 'POSITION, TRANS, TRANS_WAIT, SANS, SANS_WAIT, PERIOD, SAMPLE_IDENTIFICATION_STRING, BACKGROUND, THICKNESS, TEMPERATURE, TEMPERATURE2, FIELD, SHEAR_RATE_1, SHEAR_RATE_2, SHEAR_ANGLE_1, SHEAR_ANGLE_2, PRE_COMMAND, POST_COMMAND, RB_NUMBER' +TableEntries = "POSITION, TRANS, TRANS_WAIT, SANS, SANS_WAIT, PERIOD, SAMPLE_IDENTIFICATION_STRING, BACKGROUND, THICKNESS, TEMPERATURE, TEMPERATURE2, FIELD, SHEAR_RATE_1, SHEAR_RATE_2, SHEAR_ANGLE_1, SHEAR_ANGLE_2, PRE_COMMAND, POST_COMMAND, RB_NUMBER" data=None @@ -33,7 +33,7 @@ def popupFocusOut(event): # Default actions from the original class popupmenu.add_command(label="Sort by " + collabel, command=lambda: self.table.sortTable(currcol)) - popupmenu.add_command(label="Sort by " + collabel + ' (descending)', command=lambda: self.table.sortTable(currcol, reverse=1)) + popupmenu.add_command(label="Sort by " + collabel + " (descending)", command=lambda: self.table.sortTable(currcol, reverse=1)) #popupmenu.add_command(label="Delete This Column", command=self.table.deleteColumn) # Adding a custom command @@ -53,9 +53,9 @@ class TableImporter: def __init__(self): """Setup globals""" - self.separator_list = [',', ' ', '\t', ':'] + self.separator_list = [",", " ", "\t", ":"] self.var_sep = StringVar() - self.var_sep.set(',') + self.var_sep.set(",") return @staticmethod @@ -63,13 +63,13 @@ def ImportTableData(table, sep=None): """Import table data from a comma separated file and populate the table model""" parent = table.parentframe - filename = filedialog.askopenfilename(parent=parent, defaultextension='.csv', + filename = filedialog.askopenfilename(parent=parent, defaultextension=".csv", filetypes=[("CSV files", "*.csv")]) if not filename: return if sep is None: - sep = ',' + sep = "," if not os.path.isfile(filename): return None @@ -102,19 +102,19 @@ def show(self, callback=None): self.tablerowheader = CustomRowHeader(self.parentframe, self, width=self.rowheaderwidth) self.tablecolheader = CustomColumnHeader(self.parentframe, self) self.Yscrollbar = AutoScrollbar(self.parentframe,orient=VERTICAL,command=self.set_yviews) - self.Yscrollbar.grid(row=1,column=2,rowspan=1,sticky='news',pady=0,ipady=0) + self.Yscrollbar.grid(row=1,column=2,rowspan=1,sticky="news",pady=0,ipady=0) self.Xscrollbar = AutoScrollbar(self.parentframe,orient=HORIZONTAL,command=self.set_xviews) - self.Xscrollbar.grid(row=2,column=1,columnspan=1,sticky='news') - self['xscrollcommand'] = self.Xscrollbar.set - self['yscrollcommand'] = self.Yscrollbar.set - self.tablecolheader['xscrollcommand'] = self.Xscrollbar.set - self.tablerowheader['yscrollcommand'] = self.Yscrollbar.set + self.Xscrollbar.grid(row=2,column=1,columnspan=1,sticky="news") + self["xscrollcommand"] = self.Xscrollbar.set + self["yscrollcommand"] = self.Yscrollbar.set + self.tablecolheader["xscrollcommand"] = self.Xscrollbar.set + self.tablerowheader["yscrollcommand"] = self.Yscrollbar.set self.parentframe.rowconfigure(1,weight=1) self.parentframe.columnconfigure(1,weight=1) - self.tablecolheader.grid(row=0,column=1,rowspan=1,sticky='news',pady=0,ipady=0) - self.tablerowheader.grid(row=1,column=0,rowspan=1,sticky='news',pady=0,ipady=0) - self.grid(row=1,column=1,rowspan=1,sticky='news',pady=0,ipady=0) + self.tablecolheader.grid(row=0,column=1,rowspan=1,sticky="news",pady=0,ipady=0) + self.tablerowheader.grid(row=1,column=0,rowspan=1,sticky="news",pady=0,ipady=0) + self.grid(row=1,column=1,rowspan=1,sticky="news",pady=0,ipady=0) self.adjustColumnWidths() self.redrawTable(callback=callback) @@ -162,7 +162,7 @@ def fillDown(self, rowlist, collist): """Fill down a column, or multiple columns""" model = self.model #absrow = self.get_AbsoluteRow(rowlist[0]) - #remove first element as we don't want to overwrite it + #remove first element as we don"t want to overwrite it #rowlist.remove(rowlist[0]) return @@ -178,7 +178,7 @@ def display_sheet_data(self,name=None): sheet_name = name if sheet_name and self.workbook: sheet = self.workbook[sheet_name] - #TableEntries = 'col1, col2, label, col3, col4, label2, col5, col6, label3' + #TableEntries = "col1, col2, label, col3, col4, label2, col5, col6, label3" constructor = TableDataConstructor(TableEntries) data = constructor.construct_data(sheet.iter_rows(values_only=True)) self.data = data @@ -190,7 +190,7 @@ def importTable(self): self.importCSV() - def importCSV(self, filename=None, sep=','): + def importCSV(self, filename=None, sep=","): """Import from csv file""" importer = TableImporter() @@ -205,22 +205,22 @@ def do_bindings(self): self.bind("", self.handle_left_shift_click) self.bind("", self.handle_left_release) - if self.ostyp=='mac': + if self.ostyp=="mac": #For mac we bind Shift, left-click to right click self.bind("", self.handle_right_click) - self.bind('',self.handle_right_click) + self.bind("",self.handle_right_click) else: self.bind("", self.handle_right_click) - self.bind('', self.handle_mouse_drag) - self.bind('', self.handle_motion) + self.bind("", self.handle_mouse_drag) + self.bind("", self.handle_motion) self.bind_all("", self.deleteRow) self.bind_all("", self.addRow) self.bind_all("", self.clearData) self.bind_all("", self.paste) - #if not hasattr(self,'parentapp'): + #if not hasattr(self,"parentapp"): # self.parentapp = self.parentframe self.parentframe.master.bind_all("", self.handle_arrow_keys) @@ -232,16 +232,16 @@ def do_bindings(self): self.parentframe.master.bind_all("", self.handle_arrow_keys) self.parentframe.master.bind_all("", self.move_to_next_row) - #if 'windows' in self.platform: + #if "windows" in self.platform: self.bind("", self.mouse_wheel) - self.bind('', self.mouse_wheel) - self.bind('', self.mouse_wheel) + self.bind("", self.mouse_wheel) + self.bind("", self.mouse_wheel) self.focus_set() return def move_to_next_row(self, event): - """Move to the next row when enter is pressed and create a new row if it doesn't exist""" + """Move to the next row when enter is pressed and create a new row if it doesn"t exist""" current_row = self.getSelectedRow() if current_row >= self.rows - 1: self.addRow() @@ -249,7 +249,7 @@ def move_to_next_row(self, event): self.drawSelectedRect(self.currentrow, self.currentcol) self.drawSelectedRow() coltype = self.model.getColumnType(self.currentcol) - if coltype == 'text' or coltype == 'number': + if coltype == "text" or coltype == "number": self.drawCellEntry(self.currentrow, self.currentcol) def popupMenu(self, event, rows=None, cols=None, outside=None): @@ -258,8 +258,8 @@ def popupMenu(self, event, rows=None, cols=None, outside=None): # Default actions from the original class defaultactions = { - #"Set Fill Color": lambda: self.setcellColor(rows, cols, key='bg'), - #"Set Text Color": lambda: self.setcellColor(rows, cols, key='fg'), + #"Set Fill Color": lambda: self.setcellColor(rows, cols, key="bg"), + #"Set Text Color": lambda: self.setcellColor(rows, cols, key="fg"), "Copy": lambda: self.copyCell(rows, cols), "Paste": lambda: self.pasteCell(rows, cols), "Fill Down": lambda: self.fillDown(rows, cols), @@ -278,7 +278,7 @@ def popupMenu(self, event, rows=None, cols=None, outside=None): #defaultactions["Custom Command"] = self.customCommand main = [ "Copy", "Paste", "Fill Down", "Clear Data"] - general = ["Select All", "Add Row(s)", "Delete Row(s)", "Preferences",'New', 'Import Table', 'Export Table', 'Export Reductionbatch'] + general = ["Select All", "Add Row(s)", "Delete Row(s)", "Preferences","New", "Import Table", "Export Table", "Export Reductionbatch"] def createSubMenu(parent, label, commands): @@ -308,7 +308,7 @@ def popupFocusOut(event): def add_defaultcommands(): """now add general actions for all cells""" for action in main: - if action == 'Fill Down' and (rows == None or len(rows) <= 1): + if action == "Fill Down" and (rows == None or len(rows) <= 1): continue else: popupmenu.add_command(label=action, command=defaultactions[action]) @@ -333,7 +333,7 @@ def add_defaultcommands(): class TableWidget(Frame): """Frame for the table""" - #TableEntries = 'col1, col2, label, col3, col4, label2, col5, col6, label3' + #TableEntries = "col1, col2, label, col3, col4, label2, col5, col6, label3" constructor = TableDataConstructor(TableEntries) EMPTY_DATA = constructor.construct_empty_data() @@ -347,10 +347,10 @@ def __init__(self, parent=None, data=None): data = TableWidget.EMPTY_DATA self.data = data self.table = CustomTableCanvas(self, data=self.data) - #table.importCSV('test.csv') + #table.importCSV("test.csv") print (self.table.model.columnNames) - #table.model.data[1]['a'] = 'XX' - #table.model.setValueAt('YY',0,2) + #table.model.data[1]["a"] = "XX" + #table.model.setValueAt("YY",0,2) self.table.show() @@ -368,8 +368,8 @@ def customOption(self): if __name__ == "__main__": root = Tk() - root.geometry('800x500+200+100') - root.title('Test') + root.geometry("800x500+200+100") + root.title("Test") if data==None: data = TableWidget.EMPTY_DATA app = TableWidget(root, data=data) diff --git a/ViewModels/InstrumentVM.py b/ViewModels/InstrumentVM.py index 4ff6867..5231a96 100644 --- a/ViewModels/InstrumentVM.py +++ b/ViewModels/InstrumentVM.py @@ -1,6 +1,7 @@ import tkinter as tk from tkinter import StringVar, BooleanVar from Models.A2Setting import A2Setting +from Models.DataCollection import DataCollection from Models.InstrumentInfo import InstrumentInfo from Models.Estimation import Estimation from Models.ExperimentSettings import ExperimentSettings @@ -9,65 +10,27 @@ class InstrumentVM: - def __init__(self, settings, estimation): - #def __init__(self, experiment, estimate): - self.settings = settings - self.estimation = estimation - #self.estimate = estimate - #self.experiment = experiment - + def __init__(self, experimentsettings=None, estimation=None): + #def __init__(self, experiment, estimate): + self.settings = experimentsettings if experimentsettings is not None else ExperimentSettings() + self.estimation = estimation if estimation is not None else Estimation() - self.loq_aperture_sizes = [A2Setting.LARGE, A2Setting.MEDIUM, A2Setting.SMALL] - self.sans2d_aperture_sizes = [A2Setting.X_LARGE, A2Setting.LARGE, A2Setting.MEDIUM, A2Setting.SMALL] - self.larmor_aperture_sizes = [A2Setting.LARMOR_LARGE,A2Setting.LARMOR_MEDIUM,A2Setting.LARMOR_SMALL] - self.zoom_aperture_sizes = [A2Setting.LARGE, A2Setting.MEDIUM, A2Setting.SMALL] - self.is_instrument = StringVar(value="LARMOR") - ''' + + """ self.is_larmor = False self.is_loq = False self.is_sans2d = False self.is_zoom = False - ''' - - self.is_instrument.trace_add("write", self.update_instrument) - self.is_instrument.trace_add("write", self.on_property_changed) - - self.update_instrument() - - def update_instrument(self, *args): - if self.is_instrument.get() == "LARMOR": - self.set_instrument(self.is_instrument.get(), "A1, S1 Setting:", self.larmor_aperture_sizes, 40) - elif self.is_instrument.get() == "LOQ": - self.set_instrument(self.is_instrument.get(), "A2 Setting:", self.loq_aperture_sizes, 170) - elif self.is_instrument.get() == "SANS2D": - self.set_instrument(self.is_instrument.get(), "A6 Setting:", self.sans2d_aperture_sizes, 40) - elif self.is_instrument.get() == "ZOOM": - self.set_instrument(self.is_instrument.get(), "A6 Setting:", self.zoom_aperture_sizes, 40) - - def set_instrument(self, instrument, a2_label, a2_choices, count_rate): - self.is_instrument.set(instrument) - - InstrumentInfo.script_generator = ScriptGenerator(instrument) - - InstrumentInfo.a2_label = a2_label - InstrumentInfo.a2_choices = a2_choices - InstrumentInfo.a2_trans_enabled = True - InstrumentInfo.collection_mode_enabled = True - self.estimation.count_rate = count_rate - self.settings=Settings() - self.settings.a2_setting_sans = Settings.a2_setting_sans - self.settings.a2_setting_trans = Settings.a2_setting_trans - - def on_property_changed(self, *args): - print(f"Property {args[0]} changed to "+ self.is_instrument.get()) + """ + self.settings.update_instrument() if __name__ == "__main__": root = tk.Tk() - settings = ExperimentSettings() + experimentsettings = ExperimentSettings() estimation = Estimation() - vm = InstrumentVM(settings, estimation) + vm = InstrumentVM(experimentsettings, estimation) - vm.is_instrument.set('loq') + vm.is_instrument.set("loq") root.mainloop() \ No newline at end of file diff --git a/ViewModels/SettingsPanelVM.py b/ViewModels/SettingsPanelVM.py index 420385f..51fd0c9 100644 --- a/ViewModels/SettingsPanelVM.py +++ b/ViewModels/SettingsPanelVM.py @@ -9,14 +9,13 @@ from Models.Geometry import Geometry class SettingsPanelVM: - def __init__(self, inst_info=None): + def __init__(self, inst_info=None, experiment=None): self.property_changed = [] self.order_choices = RunOrder.get_choices() self.geometry_choices = Geometry.get_choices() - self.collection_mode_choices = DataCollection.get_choices() + #self.collection_mode_choices = DataCollection.get_choices() - - self._experiment = ExperimentSettings() + self._experiment = experiment if experiment is not None else ExperimentSettings() self._inst_info = inst_info if inst_info is not None else InstrumentInfo() def on_property_changed(self, name): @@ -48,11 +47,11 @@ def order_choices(): @staticmethod def geometry_choices(): return Geometry.get_choices() - + ''' @staticmethod def collection_mode_choices(): - return DataCollection.choices() - + return DataCollection.get_choices() + ''' diff --git a/app_config.ini b/app_config.ini deleted file mode 100644 index 38503b7..0000000 --- a/app_config.ini +++ /dev/null @@ -1,15 +0,0 @@ -[DEFAULT] -LastInstrument = 'LARMOR' -SampleHeight = 11 -SampleWidth = 11 -Geometry = Flat Plate -RunOrder = Alternate - TRANS first -NumTrans = 1 -NumSans = 1 -LoopPerRun = False -A2SettingSans = 'Large = 25, 16 mm' -A2SettingTrans = 'Large = 25, 16 mm' -CollectionMode = 'Events' -TableEntries = 'POSITION, TRANS, TRANS_WAIT, SANS, SANS_WAIT, PERIOD, SAMPLE_IDENTIFICATION_STRING, BACKGROUND, THICKNESS, TEMPERATURE, TEMPERATURE2, FIELD, SHEAR_RATE_1, SHEAR_RATE_2, SHEAR_ANGLE_1, SHEAR_ANGLE_2, PRE_COMMAND, POST_COMMAND, RB_NUMBER' -Count_Rate=38 -Move_Time=120 From bef59baee773ff12e104cdad9a96df305e966dd1 Mon Sep 17 00:00:00 2001 From: Dirk Honecker Date: Mon, 30 Dec 2024 10:26:15 +0000 Subject: [PATCH 59/88] Fix closing App, print scripts --- App.py | 5 +++++ Dialogs/PreviewDialog.py | 13 ++++++++----- MainWindow.py | 12 ++++++------ Scripting/ScriptGenerator.py | 2 +- 4 files changed, 20 insertions(+), 12 deletions(-) diff --git a/App.py b/App.py index 27e06fe..c0f82eb 100644 --- a/App.py +++ b/App.py @@ -8,10 +8,15 @@ def __init__(self): self.main_window.grid(sticky="nsew") self.root.grid_rowconfigure(1, weight=1) self.root.grid_columnconfigure(1, weight=1) + self.root.protocol("WM_DELETE_WINDOW", self.on_close) + def run(self): self.root.mainloop() + def on_close(self): + self.main_window.on_close() + if __name__ == "__main__": app = App() app.run() \ No newline at end of file diff --git a/Dialogs/PreviewDialog.py b/Dialogs/PreviewDialog.py index 4138fa3..3b03c45 100644 --- a/Dialogs/PreviewDialog.py +++ b/Dialogs/PreviewDialog.py @@ -4,8 +4,10 @@ class PreviewDialog(tk.Toplevel): - def __init__(self, parent): + def __init__(self, parent, script=None): super().__init__(parent) + self.parent = parent # Store reference to the parent (main window) + self.title("PreviewDialog") self.geometry("600x500") @@ -20,8 +22,8 @@ def __init__(self, parent): self.txt_script = scrolledtext.ScrolledText(border_frame, wrap=tk.WORD) self.txt_script.grid(row=0, column=0, sticky="nsew") - self.script_text.insert(tk.END, self.txt_script) - self.script_text.config(state=tk.DISABLED) + self.txt_script.insert(tk.END, script) + self.txt_script.config(state=tk.DISABLED) # Create a frame for the buttons button_frame = tk.Frame(self) button_frame.grid(row=1, column=0, pady=5, sticky="e") @@ -40,8 +42,9 @@ def set_script(self, script): self.txt_script.insert(tk.END, script) def print_script(self): - # Implement the print functionality here - pass + # Call the pnl_settings_on_write_clicked method from the main window + self.parent.pnl_settings_on_write_clicked() + def show(self): self.grab_set() self.wait_window() diff --git a/MainWindow.py b/MainWindow.py index 8af2463..25d20ae 100644 --- a/MainWindow.py +++ b/MainWindow.py @@ -109,13 +109,13 @@ def pnl_settings_on_write_clicked(self): filepath = filedialog.asksaveasfilename(defaultextension=filetypes[0][1], filetypes=filetypes) if filepath: scriptname = filepath.split("/")[-1].replace(" ", "_").rsplit(".", 1)[0] - script_generator= ScriptGenerator() + script_generator= ScriptGenerator(self.instrument_var.get()) script = script_generator.generate_script(self.data.runs, self.settings.experiment, scriptname) with open(filepath, "w") as file: file.write(script) def pnl_settings_on_preview_clicked(self): - script_generator= ScriptGenerator() + script_generator= ScriptGenerator(self.instrument_var.get()) script = script_generator.generate_script(self.data.runs, self.settings.experiment, "my_script") print(script) preview_dialog = PreviewDialog(self, script) @@ -139,7 +139,7 @@ def btn_open_excel_click(self): messagebox.showerror("SANS Script - Error", "Could not open Excel spreadsheet") def mnu_exit_click(self): - self.destroy() + self.on_close() def mnu_export_to_excel_click(self): excel = ExcelIO() @@ -157,8 +157,8 @@ def mnu_save_csv_click(self): def on_close(self): # Save last_instrument to settings settings = Settings() - settings.last_instrument(self.instrument_var) - self.destroy() + settings.save() + self.parent.destroy() @@ -171,5 +171,5 @@ def on_close(self): app.grid(sticky="nsew") root.grid_rowconfigure(0, weight=1) root.grid_columnconfigure(0, weight=1) - #app.protocol("WM_DELETE_WINDOW", app.on_close) + app.protocol("WM_DELETE_WINDOW", app.on_close) app.mainloop() \ No newline at end of file diff --git a/Scripting/ScriptGenerator.py b/Scripting/ScriptGenerator.py index 597628c..16e05fc 100644 --- a/Scripting/ScriptGenerator.py +++ b/Scripting/ScriptGenerator.py @@ -10,7 +10,7 @@ from Models.A2Setting import A2Setting class ScriptGenerator: - def __init__(self, instrument): + def __init__(self, instrument=None): self.indent = " " self.is_instrument = tk.StringVar() # Initialize the attribute self.is_instrument.set(instrument) From 338f343f64c4a4db2e356dc7a6df6d2602fe57ee Mon Sep 17 00:00:00 2001 From: Dirk Honecker Date: Mon, 30 Dec 2024 11:03:57 +0000 Subject: [PATCH 60/88] Clean up cs files --- App.config.py | 23 - App.xaml | 8 - App.xaml.cs | 17 - Dialogs/PreviewDialog.xaml | 23 - Dialogs/PreviewDialog.xaml.cs | 40 -- EstimationPanel.xaml | 40 -- EstimationPanel.xaml.cs | 38 -- IO/CsvExporter.cs | 93 --- IO/ExcelIO.cs | 352 ---------- MainWindow.xaml | 62 -- MainWindow.xaml.cs | 197 ------ Models/A2Setting.cs | 24 - Models/DataCollection.cs | 26 - Models/Estimation.cs | 115 ---- Models/Experiment.cs | 290 -------- Models/ExperimentSettings.cs | 292 -------- Models/Geometry.cs | 25 - Models/InstrumentInfo.cs | 98 --- Models/RunOrder.cs | 25 - Models/WaitForUnits.cs | 25 - Properties/AssemblyInfo.cs | 55 -- Properties/Resources.Designer.cs | 63 -- Properties/Resources.Designer.py | 31 - Properties/Resources.ini | 16 - Properties/Resources.resx | 117 ---- Properties/Settings.Designer.cs | 158 ----- Properties/Settings.settings | 39 -- SANS_Script_GUI_v4.csproj | 185 ------ SANS_Script_GUI_v4.csproj.py | 174 ----- SANS_Script_GUI_v4.csproj.user.py | 16 - SANS_Script_GUI_v4.xml | 2 - SANS_Script_Writer/SANS_Script_Writer.sln | 20 - .../SANS_Script_Writer/SANS_Script_Writer.py | 1 - .../SANS_Script_Writer.pyproj | 35 - Scripting/GeniePythonScriptGenerator.cs | 540 --------------- Scripting/IScriptGenerator.cs | 11 - Scripting/OpenGenieScriptGenerator.cs | 627 ------------------ SettingsPanel.xaml | 72 -- SettingsPanel.xaml.cs | 47 -- ViewModels/DataGridVM.cs | 41 -- ViewModels/EstimationPanelVM.cs | 24 - ViewModels/InstrumentVM.cs | 179 ----- ViewModels/InstrumentVM.py | 36 - ViewModels/SettingsPanelVM.cs | 87 --- 44 files changed, 4389 deletions(-) delete mode 100644 App.config.py delete mode 100644 App.xaml delete mode 100644 App.xaml.cs delete mode 100644 Dialogs/PreviewDialog.xaml delete mode 100644 Dialogs/PreviewDialog.xaml.cs delete mode 100644 EstimationPanel.xaml delete mode 100644 EstimationPanel.xaml.cs delete mode 100644 IO/CsvExporter.cs delete mode 100644 IO/ExcelIO.cs delete mode 100644 MainWindow.xaml delete mode 100644 MainWindow.xaml.cs delete mode 100644 Models/A2Setting.cs delete mode 100644 Models/DataCollection.cs delete mode 100644 Models/Estimation.cs delete mode 100644 Models/Experiment.cs delete mode 100644 Models/ExperimentSettings.cs delete mode 100644 Models/Geometry.cs delete mode 100644 Models/InstrumentInfo.cs delete mode 100644 Models/RunOrder.cs delete mode 100644 Models/WaitForUnits.cs delete mode 100644 Properties/AssemblyInfo.cs delete mode 100644 Properties/Resources.Designer.cs delete mode 100644 Properties/Resources.Designer.py delete mode 100644 Properties/Resources.ini delete mode 100644 Properties/Resources.resx delete mode 100644 Properties/Settings.Designer.cs delete mode 100644 Properties/Settings.settings delete mode 100644 SANS_Script_GUI_v4.csproj delete mode 100644 SANS_Script_GUI_v4.csproj.py delete mode 100644 SANS_Script_GUI_v4.csproj.user.py delete mode 100644 SANS_Script_GUI_v4.xml delete mode 100644 SANS_Script_Writer/SANS_Script_Writer.sln delete mode 100644 SANS_Script_Writer/SANS_Script_Writer/SANS_Script_Writer.py delete mode 100644 SANS_Script_Writer/SANS_Script_Writer/SANS_Script_Writer.pyproj delete mode 100644 Scripting/GeniePythonScriptGenerator.cs delete mode 100644 Scripting/IScriptGenerator.cs delete mode 100644 Scripting/OpenGenieScriptGenerator.cs delete mode 100644 SettingsPanel.xaml delete mode 100644 SettingsPanel.xaml.cs delete mode 100644 ViewModels/DataGridVM.cs delete mode 100644 ViewModels/EstimationPanelVM.cs delete mode 100644 ViewModels/InstrumentVM.cs delete mode 100644 ViewModels/InstrumentVM.py delete mode 100644 ViewModels/SettingsPanelVM.cs diff --git a/App.config.py b/App.config.py deleted file mode 100644 index a8c5a2b..0000000 --- a/App.config.py +++ /dev/null @@ -1,23 +0,0 @@ -import configparser - -config = configparser.ConfigParser() - -config['DEFAULT'] = { - 'LastInstrument': 'LARMOR', - 'SampleHeight': '11', - 'SampleWidth': '11', - 'Geometry': 'Flat Plate', - 'RunOrder': 'Alternate - TRANS first', - 'NumTrans': '1', - 'NumSans': '1', - 'LoopPerRun': 'False', - 'A2SettingSans': 'Large = 25, 16 mm', - 'A2SettingTrans': 'Large = 25, 16 mm', - 'CollectionMode': 'Events', - 'TableEntries': 'POSITION, TRANS, TRANS_WAIT, SANS, SANS_WAIT, PERIOD, SAMPLE_IDENTIFICATION_STRING, BACKGROUND, THICKNESS, TEMPERATURE, TEMPERATURE2, FIELD, SHEAR_RATE_1, SHEAR_RATE_2, SHEAR_ANGLE_1, SHEAR_ANGLE_2, PRE_COMMAND, POST_COMMAND, RB_NUMBER', - 'count_rate': '38', - 'move_time': '120' -} - -with open('app_config.ini', 'w') as configfile: - config.write(configfile) \ No newline at end of file diff --git a/App.xaml b/App.xaml deleted file mode 100644 index 704a2c1..0000000 --- a/App.xaml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - diff --git a/App.xaml.cs b/App.xaml.cs deleted file mode 100644 index b99238d..0000000 --- a/App.xaml.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Configuration; -using System.Data; -using System.Linq; -using System.Threading.Tasks; -using System.Windows; - -namespace LOQ_Script_Gui -{ - /// - /// Interaction logic for App.xaml - /// - public partial class App : Application - { - } -} diff --git a/Dialogs/PreviewDialog.xaml b/Dialogs/PreviewDialog.xaml deleted file mode 100644 index b85e3c9..0000000 --- a/Dialogs/PreviewDialog.xaml +++ /dev/null @@ -1,23 +0,0 @@ - - - - - - - - - - - - - - - - - - - - diff --git a/Dialogs/PreviewDialog.xaml.cs b/Dialogs/PreviewDialog.xaml.cs deleted file mode 100644 index 9edb2c7..0000000 --- a/Dialogs/PreviewDialog.xaml.cs +++ /dev/null @@ -1,40 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Windows; -using System.Windows.Controls; -using System.Windows.Data; -using System.Windows.Documents; -using System.Windows.Input; -using System.Windows.Media; -using System.Windows.Media.Imaging; -using System.Windows.Shapes; -using System.IO; -using System.Windows.Xps.Packaging; -using System.Windows.Documents; - -namespace LOQ_Script_Gui.Dialogs -{ - /// - /// Interaction logic for PreviewDialog.xaml - /// - public partial class PreviewDialog : Window - { - public PreviewDialog() - { - InitializeComponent(); - } - - public void SetScript(string script) - { - txtScript.Text = script; - } - - private void Button_Click(object sender, RoutedEventArgs e) - { - this.Close(); - } - } -} diff --git a/EstimationPanel.xaml b/EstimationPanel.xaml deleted file mode 100644 index 214c556..0000000 --- a/EstimationPanel.xaml +++ /dev/null @@ -1,40 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/EstimationPanel.xaml.cs b/EstimationPanel.xaml.cs deleted file mode 100644 index 43a5813..0000000 --- a/EstimationPanel.xaml.cs +++ /dev/null @@ -1,38 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Windows; -using System.Windows.Controls; -using System.Windows.Data; -using System.Windows.Documents; -using System.Windows.Input; -using System.Windows.Media; -using System.Windows.Media.Imaging; -using System.Windows.Navigation; -using System.Windows.Shapes; - -namespace LOQ_Script_Gui -{ - /// - /// Interaction logic for EstimationPanel.xaml - /// - public partial class EstimationPanel : UserControl - { - public event EventHandler OnCalculateClicked; - - public EstimationPanel() - { - InitializeComponent(); - } - - private void btnCalculate_Click(object sender, RoutedEventArgs e) - { - if (OnCalculateClicked != null) - { - OnCalculateClicked(sender, e); - } - } - } -} diff --git a/IO/CsvExporter.cs b/IO/CsvExporter.cs deleted file mode 100644 index d732c1b..0000000 --- a/IO/CsvExporter.cs +++ /dev/null @@ -1,93 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Collections.ObjectModel; -using System.IO; - -namespace LOQ_Script_Gui -{ - class CsvExporter - { - public static void Export(string file, ObservableCollection runs) - { - using(StreamWriter sw = new StreamWriter(file)) - { - foreach(Experiment exp in runs) - { - // Position - WriteParameter(sw, exp.Position); - - // Trans - WriteParameter(sw, exp.Trans.ToString()); - - // Trans wait - WriteParameter(sw, exp.TransWait); - - // Sans - WriteParameter(sw, exp.Sans.ToString()); - - // Sans wait - WriteParameter(sw, exp.SansWait); - - // Period - WriteParameter(sw, exp.Period); - - // Sample ID - WriteParameter(sw, exp.Sample); - - // Sample ID - WriteParameter(sw, exp.Background); - - // Thickness - WriteParameter(sw, exp.Thickness); - - // Temperature 1 - WriteParameter(sw, exp.Temperature1); - - // Temperature 2 - WriteParameter(sw, exp.Temperature2); - - // Field - WriteParameter(sw, exp.Field); - - // Shear rate 1 - WriteParameter(sw, exp.ShearRate1); - - // Shear rate 2 - WriteParameter(sw, exp.ShearRate2); - - // Shear angle 1 - WriteParameter(sw, exp.ShearAngle1); - - // Shear angle 2 - WriteParameter(sw, exp.ShearAngle2); - - // Pre-command - WriteParameter(sw, exp.PreCommand); - - // Post-command - WriteParameter(sw, exp.PostCommand); - - // RB - WriteParameter(sw, exp.RbNumber); - - sw.WriteLine(); - } - } - } - - private static void WriteParameter(StreamWriter sw, string par) - { - if (!string.IsNullOrWhiteSpace(par)) - { - sw.Write(string.Format("{0}, ", par)); - } - else - { - sw.Write(", "); - } - } - } -} diff --git a/IO/ExcelIO.cs b/IO/ExcelIO.cs deleted file mode 100644 index 13ed41f..0000000 --- a/IO/ExcelIO.cs +++ /dev/null @@ -1,352 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Data.OleDb; -using System.Data; -using System.IO; -using System.Reflection; -using System.Collections.ObjectModel; -using System.Windows; - -namespace LOQ_Script_Gui -{ - class ExcelIO - { - static List columns = new List() - { - "POSITION", "TRANS", "TRANS_WAIT_FOR", "SANS", "SANS_WAIT_FOR", "PERIOD", "SAMPLE_IDENTIFICATION_STRING", "BACKGROUND", - "THICKNESS", "TEMPERATURE", "TEMPERATURE2", "FIELD", "SHEAR_RATE_1", "SHEAR_RATE_2", "SHEAR_ANGLE_1", "SHEAR_ANGLE_2", - "PRE_COMMAND", "POST_COMMAND", "RB_NUMBER" - }; - - // The last column used in Excel - static string LastColumn = "S"; - - DataSet dataset = new DataSet(); - - public void OpenExcelWorkBook(string filename) - { - // Create connection string variable - // This is the 32 bit office version requiring Microsoft Access Database Engine 2010 Redistributable - String sConnectionString1 = "Provider=Microsoft.Jet.OLEDB.4.0;" + - "Data Source=" + filename + ";" + - "Extended Properties='Excel 8.0;HDR=Yes;IMEX=1;'"; - // if 64 bit office is installed try this one Microsoft Access Database Engine 2016 Redistributable with _X64 - String sConnectionString2 = "Provider=Microsoft.ACE.OLEDB.12.0;" + - "Data Source=" + filename + ";" + - "Mode=Read;Extended Properties='Excel 12.0;HDR=Yes;IMEX=1;'"; - - // Create connection object - OleDbConnection conn = new OleDbConnection(sConnectionString1); - OleDbConnection conn2 = new OleDbConnection(sConnectionString2); - - try - { - // Open connection - conn.Open(); - - String[] names = GetWorkSheetNames(conn); - // Create new OleDbCommand to return data from the first worksheet - OleDbCommand cmd = new OleDbCommand(@"SELECT * FROM [" + names[0] + "]", conn); - - // Create new OleDbDataAdapter that is used to build a DataSet - // based on the preceding SQL SELECT statement - OleDbDataAdapter adapter = new OleDbDataAdapter(); - - // Pass the Select command to the adapter - adapter.SelectCommand = cmd; - - // Fill the DataSet with the information from the worksheet - adapter.Fill(dataset, "XLData"); - - } - catch (Exception ex) - { - //MessageBox.Show(ex.Message, "SANS Script - Error", MessageBoxButton.OK, MessageBoxImage.Error); - try - { - // Open connection - conn2.Open(); - - String[] names = GetWorkSheetNames(conn2); - // Create new OleDbCommand to return data from the first worksheet - OleDbCommand cmd = new OleDbCommand(@"SELECT * FROM [" + names[0] + "]", conn2); - - // Create new OleDbDataAdapter that is used to build a DataSet - // based on the preceding SQL SELECT statement - OleDbDataAdapter adapter = new OleDbDataAdapter(); - - // Pass the Select command to the adapter - adapter.SelectCommand = cmd; - - // Fill the DataSet with the information from the worksheet - adapter.Fill(dataset, "XLData"); - } - catch (Exception ex1) - { - dataset = null; - MessageBox.Show(ex1.Message, "SANS Script - Error", MessageBoxButton.OK, MessageBoxImage.Error); - throw new IOException("Could not open Excel spreadsheet"); - - } - } - finally - { - // Clean up objects. - conn.Close(); - conn.Dispose(); - conn2.Close(); - conn2.Dispose(); - } - } - - public List ExtractExperiments() - { - List experiments = new List(); - - if (dataset != null) - { - if (dataset.Tables != null && dataset.Tables.Count > 0) - { - if (dataset.Tables[0].Rows != null && dataset.Tables[0].Rows.Count > 0) - { - // Loop through the rows and create an experiment for each - foreach (DataRow dr in dataset.Tables[0].Rows) - { - Experiment exp = new Experiment(); - - // Position - if (!string.IsNullOrWhiteSpace(dr[0].ToString())) - { - exp.Position = dr[0].ToString(); - } - - // Trans - if (!string.IsNullOrWhiteSpace(dr[1].ToString())) - { - exp.Trans = CastToDouble(dr[1].ToString()); - } - - // Trans wait - if (!string.IsNullOrWhiteSpace(dr[2].ToString())) - { - exp.TransWait = dr[2].ToString(); - } - - // Sans - if (!string.IsNullOrWhiteSpace(dr[3].ToString())) - { - exp.Sans = CastToDouble(dr[3].ToString()); - } - - // Sans wait - if (!string.IsNullOrWhiteSpace(dr[4].ToString())) - { - exp.SansWait = dr[4].ToString(); - } - - // Period - if (!string.IsNullOrWhiteSpace(dr[5].ToString())) - { - exp.Period = dr[5].ToString(); - } - - // Sample ID - if (!string.IsNullOrWhiteSpace(dr[6].ToString())) - { - exp.Sample = dr[6].ToString(); - } - - // Background - if (!string.IsNullOrWhiteSpace(dr[7].ToString())) - { - exp.Background = dr[7].ToString(); - } - - // Thickness - if (!string.IsNullOrWhiteSpace(dr[8].ToString())) - { - exp.Thickness = dr[8].ToString(); - } - - // Temperature 1 - if (!string.IsNullOrWhiteSpace(dr[9].ToString())) - { - exp.Temperature1 = dr[9].ToString(); - } - - // Temperature 2 - if (!string.IsNullOrWhiteSpace(dr[10].ToString())) - { - exp.Temperature2 = dr[10].ToString(); - } - - // Field - if (!string.IsNullOrWhiteSpace(dr[11].ToString())) - { - exp.Field = dr[11].ToString(); - } - - // Shear rate 1 - if (!string.IsNullOrWhiteSpace(dr[12].ToString())) - { - exp.ShearRate1 = dr[12].ToString(); - } - - // Shear rate 2 - if (!string.IsNullOrWhiteSpace(dr[13].ToString())) - { - exp.ShearRate2 = dr[13].ToString(); - } - - // Shear angle 1 - if (!string.IsNullOrWhiteSpace(dr[14].ToString())) - { - exp.ShearAngle1 = dr[14].ToString(); - } - - // Shear angle 2 - if (!string.IsNullOrWhiteSpace(dr[15].ToString())) - { - exp.ShearAngle2 = dr[15].ToString(); - } - - // Pre-command - if (!string.IsNullOrWhiteSpace(dr[16].ToString())) - { - exp.PreCommand = dr[16].ToString(); - } - - // Post-command - if (!string.IsNullOrWhiteSpace(dr[17].ToString())) - { - exp.PostCommand = dr[17].ToString(); - } - - // RB - if (!string.IsNullOrWhiteSpace(dr[18].ToString())) - { - exp.RbNumber = dr[18].ToString(); - } - - experiments.Add(exp); - } - - } - } - } - - return experiments; - } - - private static double CastToDouble(string val) - { - double result; - if (Double.TryParse(val, out result)) - { - return result; - } - - return 0.0; - } - - private static String[] GetWorkSheetNames(OleDbConnection conn) - { - System.Data.DataTable dt = null; - - try - { - // Get the data table containg the schema guid - dt = conn.GetOleDbSchemaTable(OleDbSchemaGuid.Tables, null); - - if (dt == null) - { - return null; - } - - String[] excelSheets = new String[dt.Rows.Count]; - int i = 0; - - // Add the sheet name to the string array - foreach (DataRow row in dt.Rows) - { - excelSheets[i] = row["TABLE_NAME"].ToString(); - i++; - } - - return excelSheets; - } - catch (Exception ex) - { - throw (ex); - } - finally - { - if (dt != null) - { - dt.Dispose(); - } - } - } - - public void ExportDataToExcel(ObservableCollection runs) - { - Microsoft.Office.Interop.Excel.Application xlApp; - Microsoft.Office.Interop.Excel.Workbook xlBook; - Microsoft.Office.Interop.Excel.Worksheet xlSheet; - Microsoft.Office.Interop.Excel.Range xlRange; - - try - { - // Start Excel - xlApp = new Microsoft.Office.Interop.Excel.Application(); - xlApp.Visible = true; - - // Create a workbook - xlBook = xlApp.Workbooks.Add(Missing.Value); - - // Set to page - xlSheet = (Microsoft.Office.Interop.Excel.Worksheet)xlBook.ActiveSheet; - - // Create headings - for (int i = 0; i < columns.Count; ++i) - { - xlSheet.Cells[1, i + 1] = columns[i]; - } - - xlSheet.get_Range("A1", LastColumn + "1").Font.Bold = true; - - // Enter data - for (int i = 0; i < runs.Count; ++i) - { - Experiment exp = runs[i]; - List data = new List() - { - exp.Position, exp.Trans.ToString(), exp.TransWait, exp.Sans.ToString(), exp.SansWait, - exp.Period, exp.Sample, exp.Background, exp.Thickness, exp.Temperature1, exp.Temperature2, exp.Field, - exp.ShearRate1, exp.ShearRate2, exp.ShearAngle1, exp.ShearAngle2, exp.PreCommand, - exp.PostCommand, exp.RbNumber - }; - - int row = i + 2; - var range = xlSheet.get_Range("A" + row, LastColumn + row); - range.Value = data.ToArray(); - } - - //Autofit columns - xlRange = xlSheet.get_Range("A1", LastColumn + "1"); - xlRange.EntireColumn.AutoFit(); - - //Detach - xlApp.UserControl = true; - - } - catch (Exception err) - { - throw err; - } - } - } -} diff --git a/MainWindow.xaml b/MainWindow.xaml deleted file mode 100644 index bb398d7..0000000 --- a/MainWindow.xaml +++ /dev/null @@ -1,62 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/MainWindow.xaml.cs b/MainWindow.xaml.cs deleted file mode 100644 index 03ba0f6..0000000 --- a/MainWindow.xaml.cs +++ /dev/null @@ -1,197 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Windows; -using System.Windows.Controls; -using System.Windows.Data; -using System.Windows.Documents; -using System.Windows.Input; -using System.Windows.Media; -using System.Windows.Media.Imaging; -using System.Windows.Navigation; -using System.Windows.Shapes; -using System.Collections.ObjectModel; -using System.IO; - -namespace LOQ_Script_Gui -{ - public partial class MainWindow : Window - { - DataGridVM data = new DataGridVM(); - SettingsPanelVM settings; - Estimation estimate = new Estimation(); - InstrumentInfo instInfo; - InstrumentVM instrument; - - public MainWindow() - { - InitializeComponent(); - - instInfo = new InstrumentInfo(); - settings = new SettingsPanelVM(instInfo); - - dataGrid.DataContext = data; - - pnlSettings.DataContext = settings; - pnlSettings.OnPreviewClicked += pnlSettings_OnPreviewClicked; - pnlSettings.OnWriteClicked += pnlSettings_OnWriteClicked; - - pnlEstimate.DataContext = estimate; - pnlEstimate.OnCalculateClicked += pnlEstimate_OnCalculateClicked; - - instrument = new InstrumentVM(settings.Experiment, estimate); - mnuMain.DataContext = instrument; - - string inst = Properties.Settings.Default.LastInstrument; - switch (inst) - { - case "LARMOR": - instrument.IsLarmor = true; - break; - case "LOQ": - instrument.IsLoq = true; - break; - case "SANS2D": - instrument.IsSans2d = true; - break; - case "ZOOM": - instrument.IsZoom = true; - break; - default: - instrument.IsLarmor = true; - break; - } - } - - void pnlSettings_OnWriteClicked(object sender, EventArgs e) - { - Microsoft.Win32.SaveFileDialog dlg = new Microsoft.Win32.SaveFileDialog(); - - if (instrument.IsLarmor || instrument.IsLoq || instrument.IsZoom || instrument.IsSans2d) - { - dlg.DefaultExt = ".py"; - dlg.Filter = "genie_python Files|*.py"; - } - else - { - dlg.DefaultExt = ".gcl"; - dlg.Filter = "Open Genie Files|*.gcl"; - } - - if ((bool)dlg.ShowDialog()) - { - // Use the filename provided to set the name of the script, this will be ignored by the opengenie generator - string[] filepath = dlg.FileName.Split('\\'); - int lenarr = filepath.Count(); - string scriptname = filepath[lenarr - 1]; - lenarr = scriptname.Length; - scriptname = scriptname.Substring(0, lenarr - 3); - // check for spaces and replace with underscores. - scriptname = scriptname.Replace(" ", "_"); - - string script = InstrumentInfo.ScriptGenerator.GenerateScript(data.Runs, settings.Experiment, scriptname); - - using (StreamWriter sw = new StreamWriter(dlg.FileName)) - { - sw.Write(script); - } - } - } - - void pnlSettings_OnPreviewClicked(object sender, EventArgs e) - { - string script = InstrumentInfo.ScriptGenerator.GenerateScript(data.Runs, settings.Experiment,"my_script"); - Dialogs.PreviewDialog preview = new Dialogs.PreviewDialog(); - preview.SetScript(script); - - preview.ShowDialog(); - } - - void pnlEstimate_OnCalculateClicked(object sender, EventArgs e) - { - estimate.ScriptTime = InstrumentInfo.ScriptGenerator.EstimateRunTime(data.Runs, settings.Experiment, estimate); - } - - private void btnCreateNewTable_Click(object sender, RoutedEventArgs e) - { - data.Runs.Clear(); - } - - private void btnOpenExcel_Click(object sender, RoutedEventArgs e) - { - Microsoft.Win32.OpenFileDialog dlg = new Microsoft.Win32.OpenFileDialog(); - dlg.DefaultExt = ".xlsx"; - dlg.Filter = "Excel Files|*.xls;*.xlsx"; - dlg.Multiselect = false; - - if ((bool)dlg.ShowDialog()) - { - try - { - ExcelIO excel = new ExcelIO(); - excel.OpenExcelWorkBook(dlg.FileName); - List exps = excel.ExtractExperiments(); - data.Runs = new ObservableCollection(exps); - } - catch (Exception err) - { - MessageBox.Show("Could not open Excel spreadsheet", "SANS Script - Error", MessageBoxButton.OK, MessageBoxImage.Error); - } - } - } - - private void mnuExit_Click(object sender, RoutedEventArgs e) - { - Close(); - } - - private void mnuExportToExcel_Click(object sender, RoutedEventArgs e) - { - ExcelIO excel = new ExcelIO(); - excel.ExportDataToExcel(data.Runs); - } - - private void mnuSaveCsv_Click(object sender, RoutedEventArgs e) - { - Microsoft.Win32.SaveFileDialog dlg = new Microsoft.Win32.SaveFileDialog(); - dlg.DefaultExt = ".csv"; - dlg.Filter = "CSV File|*.csv"; - - if ((bool)dlg.ShowDialog()) - { - try - { - CsvExporter.Export(dlg.FileName, data.Runs); - } - catch (Exception err) - { - MessageBox.Show("Could not save as CSV", "SANS Script - Error", MessageBoxButton.OK, MessageBoxImage.Error); - } - } - } - - private void Window_Closed(object sender, EventArgs e) - { - if (instrument.IsLarmor) - { - Properties.Settings.Default.LastInstrument = "LARMOR"; - } - else if (instrument.IsLoq) - { - Properties.Settings.Default.LastInstrument = "LOQ"; - } - else if (instrument.IsSans2d) - { - Properties.Settings.Default.LastInstrument = "SANS2D"; - } - else if (instrument.IsZoom) - { - Properties.Settings.Default.LastInstrument = "ZOOM"; - } - - Properties.Settings.Default.Save(); - } - } -} diff --git a/Models/A2Setting.cs b/Models/A2Setting.cs deleted file mode 100644 index 3111874..0000000 --- a/Models/A2Setting.cs +++ /dev/null @@ -1,24 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Collections.ObjectModel; -using System.ComponentModel; - -namespace LOQ_Script_Gui -{ - class A2Setting - { - public readonly static string XLarge = "XLarge = 20x18 mm"; - public readonly static string Large = "Large = 17 mm"; - public readonly static string Medium = "Medium = 12 mm"; - public readonly static string Small = "Small = 5/1 mm"; - public readonly static string Trans = "Trans = 1 mm"; - - public readonly static string Larmor = ""; - //public readonly static string LarmorLarge = "Large = 25, 16 mm"; - //public readonly static string LarmorMedium = "Medium = 20, 14 mm"; - //public readonly static string LarmorSmall = "Small = 15, 10 mm"; - } -} diff --git a/Models/DataCollection.cs b/Models/DataCollection.cs deleted file mode 100644 index 72fb51b..0000000 --- a/Models/DataCollection.cs +++ /dev/null @@ -1,26 +0,0 @@ -using System.Collections.ObjectModel; - -namespace LOQ_Script_Gui -{ - class DataCollection - { - public readonly static string Histogram = "Histogram"; - public readonly static string Events = "Events"; - public readonly static string SESANS = "SESANS"; - public readonly static string TRANSSANS_Events = "TRANSSANS_Events"; - public readonly static string TRANSSANS_Histogram = "TRANSSANS_Histogram"; - - static ObservableCollection choices = new ObservableCollection() - { - Histogram, TRANSSANS_Histogram, Events, TRANSSANS_Events, SESANS - }; - - public static ObservableCollection Choices - { - get - { - return choices; - } - } - } -} diff --git a/Models/Estimation.cs b/Models/Estimation.cs deleted file mode 100644 index 459a634..0000000 --- a/Models/Estimation.cs +++ /dev/null @@ -1,115 +0,0 @@ -using System.ComponentModel; -using System; - -namespace LOQ_Script_Gui -{ - class Estimation : INotifyPropertyChanged, IDataErrorInfo - { - public event PropertyChangedEventHandler PropertyChanged; - - protected void OnPropertyChanged(string name) - { - PropertyChangedEventHandler handler = PropertyChanged; - if (handler != null) - { - handler(this, new PropertyChangedEventArgs(name)); - } - } - - private double countRate = 40; - public double CountRate - { - get - { - return countRate; - } - set - { - countRate = value; - OnPropertyChanged("CountRate"); - } - } - - private double moveTime = 120; - public double MoveTime - { - get - { - return moveTime; - } - set - { - moveTime = value; - OnPropertyChanged("MoveTime"); - } - } - - private string scriptTime; - public string ScriptTime - { - get - { - return scriptTime; - } - set - { - scriptTime = value; - OnPropertyChanged("ScriptTime"); - } - } - - private bool isValid; - public bool IsValid - { - get - { - return isValid; - } - set - { - isValid = value; - OnPropertyChanged("IsValid"); - } - } - - public string Error - { - get { return String.Empty; } - } - - public string this[string columnName] - { - get - { - string errorMessage = String.Empty; - - switch (columnName) - { - case "CountRate": - if (CountRate < 0) - { - errorMessage = "Count rate cannot be negative!"; - } - break; - case "MoveTime": - if (MoveTime < 0) - { - errorMessage = "Move time cannot be negative!"; - } - break; - } - - if (string.IsNullOrWhiteSpace(errorMessage)) - { - IsValid = true; - } - else - { - IsValid = false; - } - - return errorMessage; - } - } - } -} diff --git a/Models/Experiment.cs b/Models/Experiment.cs deleted file mode 100644 index d88a6ba..0000000 --- a/Models/Experiment.cs +++ /dev/null @@ -1,290 +0,0 @@ -using System.ComponentModel; - -namespace LOQ_Script_Gui -{ - class Experiment : INotifyPropertyChanged - { - public event PropertyChangedEventHandler PropertyChanged; - - protected void OnPropertyChanged(string name) - { - PropertyChangedEventHandler handler = PropertyChanged; - if (handler != null) - { - handler(this, new PropertyChangedEventArgs(name)); - } - } - - private string position; - public string Position - { - get - { - return position; - } - set - { - position = value; - OnPropertyChanged("Position"); - } - } - - private double trans; - public double Trans - { - get - { - return trans; - } - set - { - trans = value; - OnPropertyChanged("Trans"); - } - } - - private string transWait; - public string TransWait - { - get - { - return transWait; - } - set - { - transWait = value; - OnPropertyChanged("TransWait"); - } - } - - private double sans; - public double Sans - { - get - { - return sans; - } - set - { - sans = value; - OnPropertyChanged("Sans"); - } - } - - private string sansWait; - public string SansWait - { - get - { - return sansWait; - } - set - { - sansWait = value; - OnPropertyChanged("SansWait"); - } - } - - private string period; - public string Period - { - get - { - return period; - } - set - { - period = value; - OnPropertyChanged("Period"); - } - } - - private string background; - public string Background - { - get - { - return background; - } - set - { - background = value; - OnPropertyChanged("Background"); - } - } - - private string sample; - public string Sample - { - get - { - return sample; - } - set - { - sample = value; - OnPropertyChanged("Sample"); - } - } - - private string thickness; - public string Thickness - { - get - { - return thickness; - } - set - { - thickness = value; - OnPropertyChanged("Thickness"); - } - } - - private string temperature1; - public string Temperature1 - { - get - { - return temperature1; - } - set - { - temperature1 = value; - OnPropertyChanged("Temperature1"); - } - } - - private string temperature2; - public string Temperature2 - { - get - { - return temperature2; - } - set - { - temperature2 = value; - OnPropertyChanged("Temperature2"); - } - } - - private string field; - public string Field - { - get - { - return field; - } - set - { - field = value; - OnPropertyChanged("Field"); - } - } - - private string shearRate1; - public string ShearRate1 - { - get - { - return shearRate1; - } - set - { - shearRate1 = value; - OnPropertyChanged("ShearRate1"); - } - } - - private string shearRate2; - public string ShearRate2 - { - get - { - return shearRate2; - } - set - { - shearRate2 = value; - OnPropertyChanged("ShearRate2"); - } - } - - private string shearAngle1; - public string ShearAngle1 - { - get - { - return shearAngle1; - } - set - { - shearAngle1 = value; - OnPropertyChanged("ShearAngle1"); - } - } - - private string shearAngle2; - public string ShearAngle2 - { - get - { - return shearAngle2; - } - set - { - shearAngle2 = value; - OnPropertyChanged("ShearAngle2"); - } - } - - private string preCommand; - public string PreCommand - { - get - { - return preCommand; - } - set - { - preCommand = value; - OnPropertyChanged("PreCommand"); - } - } - - private string postCommand; - public string PostCommand - { - get - { - return postCommand; - } - set - { - postCommand = value; - OnPropertyChanged("PostCommand"); - } - } - - private string rbNumber; - public string RbNumber - { - get - { - return rbNumber; - } - set - { - rbNumber = value; - OnPropertyChanged("RbNumber"); - } - } - - public Experiment() - { - sans = 0; - trans = 0; - } - } -} diff --git a/Models/ExperimentSettings.cs b/Models/ExperimentSettings.cs deleted file mode 100644 index dee32a1..0000000 --- a/Models/ExperimentSettings.cs +++ /dev/null @@ -1,292 +0,0 @@ -using System; -using System.ComponentModel; - -namespace LOQ_Script_Gui -{ - class ExperimentSettings : INotifyPropertyChanged, IDataErrorInfo - { - public event PropertyChangedEventHandler PropertyChanged; - - protected void OnPropertyChanged(string name) - { - PropertyChangedEventHandler handler = PropertyChanged; - if (handler != null) - { - handler(this, new PropertyChangedEventArgs(name)); - } - } - - //private string order = RunOrder.TransFirst; - private string order = Properties.Settings.Default.RunOrder; - - public string Order - { - get - { - return order; - } - set - { - order = value; - OnPropertyChanged("Order"); - Properties.Settings.Default.RunOrder = order; - Properties.Settings.Default.Save(); - } - } - - //private int numSans = 1; - private int numSans = Convert.ToInt16(Properties.Settings.Default.NumSans); - public int NumSans - { - get - { - return numSans; - } - set - { - numSans = value; - OnPropertyChanged("NumSans"); - Properties.Settings.Default.NumSans = Convert.ToString(numSans); - Properties.Settings.Default.Save(); - } - } - - //private int numTrans = 1; - private int numTrans = Convert.ToInt16(Properties.Settings.Default.NumTrans); - public int NumTrans - { - get - { - return numTrans; - } - set - { - numTrans = value; - OnPropertyChanged("NumTrans"); - Properties.Settings.Default.NumTrans = Convert.ToString(numTrans); - Properties.Settings.Default.Save(); - } - } - - //private bool loopPerRun; - private bool loopPerRun = Convert.ToBoolean(Properties.Settings.Default.LoopPerRun); - - public bool LoopPerRun - { - get - { - return loopPerRun; - } - set - { - loopPerRun = value; - OnPropertyChanged("LoopPerRun"); - Properties.Settings.Default.LoopPerRun = Convert.ToString(LoopPerRun); - Properties.Settings.Default.Save(); - } - } - - //private string a2SettingSans = A2Setting.Large; - private string a2SettingSans = Properties.Settings.Default.A2SettingSans; - public string A2SettingSans - { - get - { - return a2SettingSans; - } - set - { - a2SettingSans = value; - OnPropertyChanged("A2SettingSans"); - Properties.Settings.Default.A2SettingSans = a2SettingSans; - Properties.Settings.Default.Save(); - } - } - - //private string a2SettingTrans = A2Setting.Large; - private string a2SettingTrans = Properties.Settings.Default.A2SettingTrans; - public string A2SettingTrans - { - get - { - return a2SettingTrans; - } - set - { - a2SettingTrans = value; - OnPropertyChanged("A2SettingTrans"); - Properties.Settings.Default.A2SettingTrans = a2SettingTrans; - Properties.Settings.Default.Save(); - } - } - - //private string sampleGeometry = Geometry.FlatPlate; - private string sampleGeometry = Properties.Settings.Default.Geometry; - public string SampleGeometry - { - get - { - return sampleGeometry; - } - set - { - sampleGeometry = value; - - if (sampleGeometry == Geometry.Disc) - { - SampleWidthEnabled = false; - } - else - { - SampleWidthEnabled = true; - } - - OnPropertyChanged("SampleGeometry"); - Properties.Settings.Default.Geometry = sampleGeometry; - Properties.Settings.Default.Save(); - } - } - - //private double sampleHeight = 11; - private double sampleHeight = Convert.ToDouble(Properties.Settings.Default.SampleHeight); - public double SampleHeight - { - get - { - return sampleHeight; - } - set - { - sampleHeight = value; - if (!sampleWidthEnabled) - { - // Width takes the same value as height - SampleWidth = value; - } - - OnPropertyChanged("SampleHeight"); - Properties.Settings.Default.SampleHeight = Convert.ToString(sampleHeight); - Properties.Settings.Default.Save(); - } - } - - //private double sampleWidth = 11; - private double sampleWidth = Convert.ToDouble(Properties.Settings.Default.SampleWidth); - public double SampleWidth - { - get - { - return sampleWidth; - } - set - { - sampleWidth = value; - OnPropertyChanged("SampleWidth"); - Properties.Settings.Default.SampleWidth = Convert.ToString(sampleWidth); - Properties.Settings.Default.Save(); - } - } - - private bool sampleWidthEnabled = true; - public bool SampleWidthEnabled - { - get - { - return sampleWidthEnabled; - } - set - { - sampleWidthEnabled = value; - if (!value) - { - // Width takes the same value as height - SampleWidth = SampleHeight; - } - - OnPropertyChanged("SampleWidthEnabled"); - } - } - - //private string collectionMode = DataCollection.Events; - private string collectionMode = Properties.Settings.Default.CollectionMode; - public string CollectionMode - { - get - { - return collectionMode; - } - set - { - collectionMode = value; - OnPropertyChanged("CollectionMode"); - Properties.Settings.Default.CollectionMode = collectionMode; - Properties.Settings.Default.Save(); - } - } - - private bool isValid = true; - public bool IsValid - { - get - { - return isValid; - } - set - { - isValid = value; - OnPropertyChanged("IsValid"); - } - } - - public string Error - { - get { return String.Empty; } - } - - public string this[string columnName] - { - get - { - string errorMessage = String.Empty; - - switch (columnName) - { - case "NumSans": - if (NumSans < 0) - { - errorMessage = "Number of SANS runs cannot be negative!"; - } - break; - case "NumTrans": - if (NumTrans < 0) - { - errorMessage = "Number of TRANS runs cannot be negative!"; - } - break; - case "SampleHeight": - if (SampleHeight <= 0 || SampleHeight > 20) - { - errorMessage = "Sample height must be between 0 and 20"; - } - break; - case "SampleWidth": - if (SampleWidth <= 0 || SampleWidth > 20) - { - errorMessage = "Sample width must be between 0 and 20"; - } - break; - } - - if (string.IsNullOrWhiteSpace(errorMessage)) - { - IsValid = true; - } - else - { - IsValid = false; - } - return errorMessage; - } - } - } -} diff --git a/Models/Geometry.cs b/Models/Geometry.cs deleted file mode 100644 index d20abc8..0000000 --- a/Models/Geometry.cs +++ /dev/null @@ -1,25 +0,0 @@ -using System.Collections.ObjectModel; - -namespace LOQ_Script_Gui -{ - class Geometry - { - public readonly static string Disc = "Disc"; - public readonly static string Cylindrical = "Cylindrical"; - public readonly static string FlatPlate = "Flat Plate"; - public readonly static string SingleCrystal = "Single Crystal"; - - static ObservableCollection choices = new ObservableCollection() - { - Disc, Cylindrical, FlatPlate, SingleCrystal - }; - - public static ObservableCollection Choices - { - get - { - return choices; - } - } - } -} diff --git a/Models/InstrumentInfo.cs b/Models/InstrumentInfo.cs deleted file mode 100644 index 00ce2fc..0000000 --- a/Models/InstrumentInfo.cs +++ /dev/null @@ -1,98 +0,0 @@ -using System; -using System.Collections.Generic; -using System.ComponentModel; -using LOQ_Script_Gui.Scripting; -using System.Collections.ObjectModel; - -namespace LOQ_Script_Gui -{ - /// - /// Contains the information needed to set the GUI up for the specified instrument. - /// - class InstrumentInfo : INotifyPropertyChanged - { - public event PropertyChangedEventHandler PropertyChanged; - - protected void OnPropertyChanged(string name) - { - PropertyChangedEventHandler handler = PropertyChanged; - if (handler != null) - { - handler(this, new PropertyChangedEventArgs(name)); - } - } - - public static event EventHandler StaticPropertyChanged = delegate { }; - - protected static void OnStaticPropertyChanged(string propertyName) - { - if (StaticPropertyChanged != null) - StaticPropertyChanged(null, new PropertyChangedEventArgs(propertyName)); - } - - static string a2Label = "A2 Setting:"; - public static string A2Label - { - get - { - return a2Label; - } - set - { - a2Label = value; - OnStaticPropertyChanged("A2Label"); - } - } - - // Provides the combo box options - // These values can change depending on the instrument - static ObservableCollection a2Choices = new ObservableCollection() - { - A2Setting.Large, A2Setting.Medium, A2Setting.Small - }; - - public static ObservableCollection A2Choices - { - get - { - return a2Choices; - } - - set - { - a2Choices = value; - OnStaticPropertyChanged("A2Choices"); - } - } - - static bool a2TransEnabled = true; - public static bool A2TransEnabled - { - get - { - return a2TransEnabled; - } - set - { - a2TransEnabled = value; - OnStaticPropertyChanged("A2TransEnabled"); - } - } - - static bool collectionModeEnabled = false; - public static bool CollectionModeEnabled - { - get - { - return collectionModeEnabled; - } - set - { - collectionModeEnabled = value; - OnStaticPropertyChanged("CollectionModeEnabled"); - } - } - - public static IScriptGenerator ScriptGenerator { get; set; } - } -} diff --git a/Models/RunOrder.cs b/Models/RunOrder.cs deleted file mode 100644 index 8aba130..0000000 --- a/Models/RunOrder.cs +++ /dev/null @@ -1,25 +0,0 @@ -using System.Collections.ObjectModel; - -namespace LOQ_Script_Gui -{ - class RunOrder - { - public readonly static string AllTrans = "All TRANS first"; - public readonly static string TransFirst = "Alternate - TRANS first"; - public readonly static string AllSans = "All SANS first"; - public readonly static string SansFirst = "Alternate - SANS first"; - - static ObservableCollection choices = new ObservableCollection() - { - AllTrans, TransFirst, AllSans, SansFirst - }; - - public static ObservableCollection Choices - { - get - { - return choices; - } - } - } -} diff --git a/Models/WaitForUnits.cs b/Models/WaitForUnits.cs deleted file mode 100644 index 0484abf..0000000 --- a/Models/WaitForUnits.cs +++ /dev/null @@ -1,25 +0,0 @@ -using System.Collections.ObjectModel; - -namespace LOQ_Script_Gui -{ - class WaitForUnits - { - public readonly static string MicroAmps = "Microamp-hours"; - public readonly static string Minutes = "Minutes"; - public readonly static string Seconds = "Seconds"; - public readonly static string Frames = "Frames"; - - static ObservableCollection choices = new ObservableCollection() - { - MicroAmps, Minutes, Seconds, Frames - }; - - public static ObservableCollection Choices - { - get - { - return choices; - } - } - } -} diff --git a/Properties/AssemblyInfo.cs b/Properties/AssemblyInfo.cs deleted file mode 100644 index 1f2339a..0000000 --- a/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,55 +0,0 @@ -using System.Reflection; -using System.Resources; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; -using System.Windows; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("LOQ_Script_Gui")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("LOQ_Script_Gui")] -[assembly: AssemblyCopyright("Copyright © 2015")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. -[assembly: ComVisible(false)] - -//In order to begin building localizable applications, set -//CultureYouAreCodingWith in your .csproj file -//inside a . For example, if you are using US english -//in your source files, set the to en-US. Then uncomment -//the NeutralResourceLanguage attribute below. Update the "en-US" in -//the line below to match the UICulture setting in the project file. - -//[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)] - - -[assembly: ThemeInfo( - ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located - //(used if a resource is not found in the page, - // or application resource dictionaries) - ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located - //(used if a resource is not found in the page, - // app, or any theme specific resource dictionaries) -)] - - -// Version information for an assembly consists of the following four values: -// -// Major Version -// Minor Version -// Build Number -// Revision -// -// You can specify all the values or you can default the Build and Revision Numbers -// by using the '*' as shown below: -// [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.0.0.0")] -[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/Properties/Resources.Designer.cs b/Properties/Resources.Designer.cs deleted file mode 100644 index 2b67d17..0000000 --- a/Properties/Resources.Designer.cs +++ /dev/null @@ -1,63 +0,0 @@ -//------------------------------------------------------------------------------ -// -// This code was generated by a tool. -// Runtime Version:4.0.30319.42000 -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -//------------------------------------------------------------------------------ - -namespace LOQ_Script_Gui.Properties { - using System; - - - /// - /// A strongly-typed resource class, for looking up localized strings, etc. - /// - // This class was auto-generated by the StronglyTypedResourceBuilder - // class via a tool like ResGen or Visual Studio. - // To add or remove a member, edit your .ResX file then rerun ResGen - // with the /str option, or rebuild your VS project. - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - internal class Resources { - - private static global::System.Resources.ResourceManager resourceMan; - - private static global::System.Globalization.CultureInfo resourceCulture; - - [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] - internal Resources() { - } - - /// - /// Returns the cached ResourceManager instance used by this class. - /// - [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - internal static global::System.Resources.ResourceManager ResourceManager { - get { - if (object.ReferenceEquals(resourceMan, null)) { - global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("LOQ_Script_Gui.Properties.Resources", typeof(Resources).Assembly); - resourceMan = temp; - } - return resourceMan; - } - } - - /// - /// Overrides the current thread's CurrentUICulture property for all - /// resource lookups using this strongly typed resource class. - /// - [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - internal static global::System.Globalization.CultureInfo Culture { - get { - return resourceCulture; - } - set { - resourceCulture = value; - } - } - } -} diff --git a/Properties/Resources.Designer.py b/Properties/Resources.Designer.py deleted file mode 100644 index d4614f7..0000000 --- a/Properties/Resources.Designer.py +++ /dev/null @@ -1,31 +0,0 @@ -import gettext -import os - -class Resources: - def __init__(self): - self.resource_man = None - self.resource_culture = None - - @property - def resource_manager(self): - if self.resource_man is None: - locale_dir = os.path.join(os.path.dirname(__file__), 'locale') - self.resource_man = gettext.translation('resources', localedir=locale_dir, languages=[self.resource_culture]) - return self.resource_man - - @property - def culture(self): - return self.resource_culture - - @culture.setter - def culture(self, value): - self.resource_culture = value - self.resource_man = None # Reset the resource manager to reload with the new culture - -# Example usage -resources = Resources() -resources.culture = 'en_US' # Set the desired culture -_ = resources.resource_manager.gettext - -# Example of using the localized string -print(_("Hello, World!")) \ No newline at end of file diff --git a/Properties/Resources.ini b/Properties/Resources.ini deleted file mode 100644 index 0d87d37..0000000 --- a/Properties/Resources.ini +++ /dev/null @@ -1,16 +0,0 @@ -#using configparser -[resheader] -resmimetype = text/microsoft-resx -version = 2.0 -reader = System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 -writer = System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - -[data] -Name1 = this is my long string -Color1 = Blue -Bitmap1 = [base64 mime encoded serialized .NET Framework object] -Icon1 = [base64 mime encoded string representing a byte array form of the .NET Framework object] - -[metadata] -Name1.comment = this is a comment -Icon1.comment = This is a comment \ No newline at end of file diff --git a/Properties/Resources.resx b/Properties/Resources.resx deleted file mode 100644 index af7dbeb..0000000 --- a/Properties/Resources.resx +++ /dev/null @@ -1,117 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - \ No newline at end of file diff --git a/Properties/Settings.Designer.cs b/Properties/Settings.Designer.cs deleted file mode 100644 index 5f01f51..0000000 --- a/Properties/Settings.Designer.cs +++ /dev/null @@ -1,158 +0,0 @@ -//------------------------------------------------------------------------------ -// -// This code was generated by a tool. -// Runtime Version:4.0.30319.42000 -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -//------------------------------------------------------------------------------ - -namespace LOQ_Script_Gui.Properties { - - - [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "17.0.3.0")] - internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { - - private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); - - public static Settings Default { - get { - return defaultInstance; - } - } - - [global::System.Configuration.UserScopedSettingAttribute()] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Configuration.DefaultSettingValueAttribute("LARMOR")] - public string LastInstrument { - get { - return ((string)(this["LastInstrument"])); - } - set { - this["LastInstrument"] = value; - } - } - - [global::System.Configuration.UserScopedSettingAttribute()] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Configuration.DefaultSettingValueAttribute("11")] - public string SampleHeight { - get { - return ((string)(this["SampleHeight"])); - } - set { - this["SampleHeight"] = value; - } - } - - [global::System.Configuration.UserScopedSettingAttribute()] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Configuration.DefaultSettingValueAttribute("11")] - public string SampleWidth { - get { - return ((string)(this["SampleWidth"])); - } - set { - this["SampleWidth"] = value; - } - } - - [global::System.Configuration.UserScopedSettingAttribute()] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Configuration.DefaultSettingValueAttribute("Flat Plate")] - public string Geometry { - get { - return ((string)(this["Geometry"])); - } - set { - this["Geometry"] = value; - } - } - - [global::System.Configuration.UserScopedSettingAttribute()] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Configuration.DefaultSettingValueAttribute("Alternate - TRANS first")] - public string RunOrder { - get { - return ((string)(this["RunOrder"])); - } - set { - this["RunOrder"] = value; - } - } - - [global::System.Configuration.UserScopedSettingAttribute()] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Configuration.DefaultSettingValueAttribute("1")] - public string NumTrans { - get { - return ((string)(this["NumTrans"])); - } - set { - this["NumTrans"] = value; - } - } - - [global::System.Configuration.UserScopedSettingAttribute()] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Configuration.DefaultSettingValueAttribute("1")] - public string NumSans { - get { - return ((string)(this["NumSans"])); - } - set { - this["NumSans"] = value; - } - } - - [global::System.Configuration.UserScopedSettingAttribute()] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Configuration.DefaultSettingValueAttribute("False")] - public string LoopPerRun { - get { - return ((string)(this["LoopPerRun"])); - } - set { - this["LoopPerRun"] = value; - } - } - - [global::System.Configuration.UserScopedSettingAttribute()] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Configuration.DefaultSettingValueAttribute("Large = 25, 16 mm")] - public string A2SettingSans { - get { - return ((string)(this["A2SettingSans"])); - } - set { - this["A2SettingSans"] = value; - } - } - - [global::System.Configuration.UserScopedSettingAttribute()] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Configuration.DefaultSettingValueAttribute("Large = 25, 16 mm")] - public string A2SettingTrans { - get { - return ((string)(this["A2SettingTrans"])); - } - set { - this["A2SettingTrans"] = value; - } - } - - [global::System.Configuration.UserScopedSettingAttribute()] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Configuration.DefaultSettingValueAttribute("Events")] - public string CollectionMode { - get { - return ((string)(this["CollectionMode"])); - } - set { - this["CollectionMode"] = value; - } - } - } -} diff --git a/Properties/Settings.settings b/Properties/Settings.settings deleted file mode 100644 index 27069e6..0000000 --- a/Properties/Settings.settings +++ /dev/null @@ -1,39 +0,0 @@ - - - - - - LARMOR - - - 11 - - - 11 - - - Flat Plate - - - Alternate - TRANS first - - - 1 - - - 1 - - - False - - - Large = 25, 16 mm - - - Large = 25, 16 mm - - - Events - - - \ No newline at end of file diff --git a/SANS_Script_GUI_v4.csproj b/SANS_Script_GUI_v4.csproj deleted file mode 100644 index e8ed189..0000000 --- a/SANS_Script_GUI_v4.csproj +++ /dev/null @@ -1,185 +0,0 @@ - - - - - Debug - AnyCPU - {5DBFF2C3-05A0-4B4B-9080-8224D86A7B40} - WinExe - Properties - LOQ_Script_Gui - SANS_Script_Gui_v4 - v4.8 - 512 - {60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} - 4 - false - - publish\ - true - Disk - false - Foreground - 7 - Days - false - false - true - 0 - 1.0.0.%2a - false - true - - - x64 - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - false - - - x64 - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - - - SANS_Icon.ico - - - - True - - - - - - - - - - - 4.0 - - - - - - - - MSBuild:Compile - Designer - - - - - - PreviewDialog.xaml - - - - - - - - EstimationPanel.xaml - - - - - - - - - SettingsPanel.xaml - - - - - - - Designer - MSBuild:Compile - - - Designer - MSBuild:Compile - - - MSBuild:Compile - Designer - - - App.xaml - Code - - - MainWindow.xaml - Code - - - Designer - MSBuild:Compile - - - - - Code - - - True - True - Resources.resx - - - True - Settings.settings - True - - - ResXFileCodeGenerator - Resources.Designer.cs - - - SettingsSingleFileGenerator - Settings.Designer.cs - - - - - - - - - False - Microsoft .NET Framework 4.5 %28x86 and x64%29 - true - - - False - .NET Framework 3.5 SP1 - false - - - - - - - - - - - \ No newline at end of file diff --git a/SANS_Script_GUI_v4.csproj.py b/SANS_Script_GUI_v4.csproj.py deleted file mode 100644 index f312438..0000000 --- a/SANS_Script_GUI_v4.csproj.py +++ /dev/null @@ -1,174 +0,0 @@ -import xml.etree.ElementTree as ET - -root = ET.Element("Project", ToolsVersion="12.0", DefaultTargets="Build", xmlns="http://schemas.microsoft.com/developer/msbuild/2003") - -ET.SubElement(root, "Import", Project="$(MSBuildExtensionsPath)\\$(MSBuildToolsVersion)\\Microsoft.Common.props", Condition="Exists('$(MSBuildExtensionsPath)\\$(MSBuildToolsVersion)\\Microsoft.Common.props')") - -property_group = ET.SubElement(root, "PropertyGroup") -ET.SubElement(property_group, "Configuration", Condition=" '$(Configuration)' == '' ").text = "Debug" -ET.SubElement(property_group, "Platform", Condition=" '$(Platform)' == '' ").text = "AnyCPU" -ET.SubElement(property_group, "ProjectGuid").text = "{5DBFF2C3-05A0-4B4B-9080-8224D86A7B40}" -ET.SubElement(property_group, "OutputType").text = "WinExe" -ET.SubElement(property_group, "AppDesignerFolder").text = "Properties" -ET.SubElement(property_group, "RootNamespace").text = "LOQ_Script_Gui" -ET.SubElement(property_group, "AssemblyName").text = "SANS_Script_Gui_v4" -ET.SubElement(property_group, "TargetFrameworkVersion").text = "v4.8" -ET.SubElement(property_group, "FileAlignment").text = "512" -ET.SubElement(property_group, "ProjectTypeGuids").text = "{60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}" -ET.SubElement(property_group, "WarningLevel").text = "4" -ET.SubElement(property_group, "IsWebBootstrapper").text = "false" -ET.SubElement(property_group, "TargetFrameworkProfile") -ET.SubElement(property_group, "PublishUrl").text = "publish\\" -ET.SubElement(property_group, "Install").text = "true" -ET.SubElement(property_group, "InstallFrom").text = "Disk" -ET.SubElement(property_group, "UpdateEnabled").text = "false" -ET.SubElement(property_group, "UpdateMode").text = "Foreground" -ET.SubElement(property_group, "UpdateInterval").text = "7" -ET.SubElement(property_group, "UpdateIntervalUnits").text = "Days" -ET.SubElement(property_group, "UpdatePeriodically").text = "false" -ET.SubElement(property_group, "UpdateRequired").text = "false" -ET.SubElement(property_group, "MapFileExtensions").text = "true" -ET.SubElement(property_group, "ApplicationRevision").text = "0" -ET.SubElement(property_group, "ApplicationVersion").text = "1.0.0.%2a" -ET.SubElement(property_group, "UseApplicationTrust").text = "false" -ET.SubElement(property_group, "BootstrapperEnabled").text = "true" - -debug_property_group = ET.SubElement(root, "PropertyGroup", Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ") -ET.SubElement(debug_property_group, "PlatformTarget").text = "x64" -ET.SubElement(debug_property_group, "DebugSymbols").text = "true" -ET.SubElement(debug_property_group, "DebugType").text = "full" -ET.SubElement(debug_property_group, "Optimize").text = "false" -ET.SubElement(debug_property_group, "OutputPath").text = "bin\\Debug\\" -ET.SubElement(debug_property_group, "DefineConstants").text = "DEBUG;TRACE" -ET.SubElement(debug_property_group, "ErrorReport").text = "prompt" -ET.SubElement(debug_property_group, "WarningLevel").text = "4" -ET.SubElement(debug_property_group, "Prefer32Bit").text = "false" - -release_property_group = ET.SubElement(root, "PropertyGroup", Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ") -ET.SubElement(release_property_group, "PlatformTarget").text = "x64" -ET.SubElement(release_property_group, "DebugType").text = "pdbonly" -ET.SubElement(release_property_group, "Optimize").text = "true" -ET.SubElement(release_property_group, "OutputPath").text = "bin\\Release\\" -ET.SubElement(release_property_group, "DefineConstants").text = "TRACE" -ET.SubElement(release_property_group, "ErrorReport").text = "prompt" -ET.SubElement(release_property_group, "WarningLevel").text = "4" - -icon_property_group = ET.SubElement(root, "PropertyGroup") -ET.SubElement(icon_property_group, "ApplicationIcon").text = "SANS_Icon.ico" - -references = [ - ("Microsoft.Office.Interop.Excel, Version=15.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c, processorArchitecture=MSIL", "True"), - "ReachFramework", - "System", - "System.Data", - "System.Xml", - "Microsoft.CSharp", - "System.Core", - "System.Xml.Linq", - "System.Data.DataSetExtensions", - ("System.Xaml", "4.0"), - "WindowsBase", - "PresentationCore", - "PresentationFramework" -] - -item_group = ET.SubElement(root, "ItemGroup") -for ref in references: - if isinstance(ref, tuple): - reference = ET.SubElement(item_group, "Reference", Include=ref[0]) - ET.SubElement(reference, "EmbedInteropTypes").text = ref[1] - else: - ET.SubElement(item_group, "Reference", Include=ref) - -compile_files = [ - "Models\\DataCollection.py", - "Models\\InstrumentInfo.py", - "IO\\CsvExporter.py", - "Dialogs\\PreviewDialog.xaml.py", - "IO\\ExcelIO.py", - "Models\\A2Setting.py", - "Models\\Estimation.py", - "Models\\Geometry.py", - "EstimationPanel.xaml.py", - "Models\\Experiment.py", - "Models\\ExperimentSettings.py", - "Models\\WaitForUnits.py", - "Scripting\\ScriptGenerator.py", - "Scripting\\IScriptGenerator.py", - "Scripting\\OpenGenieScriptGenerator.py", - "SettingsPanel.xaml.py", - "ViewModels\\DataGridVM.py", - "ViewModels\\EstimationPanelVM.py", - "ViewModels\\InstrumentVM.py", - "ViewModels\\SettingsPanelVM.py", - "App.xaml.py", - "MainWindow.xaml.py", - "Properties\\AssemblyInfo.py", - "Properties\\Resources.Designer.py", - "Properties\\Settings.Designer.py" -] - -for file in compile_files: - compile = ET.SubElement(item_group, "Compile", Include=file) - if file.endswith(".xaml.py"): - ET.SubElement(compile, "DependentUpon").text = file.replace(".py", "") - elif file.endswith(".Designer.py"): - ET.SubElement(compile, "AutoGen").text = "True" - ET.SubElement(compile, "DesignTime").text = "True" - ET.SubElement(compile, "DependentUpon").text = file.replace(".Designer.py", ".resx") - if "Settings" in file: - ET.SubElement(compile, "DesignTimeSharedInput").text = "True" - -pages = [ - "Dialogs\\PreviewDialog.xaml", - "EstimationPanel.xaml", - "MainWindow.xaml", - "SettingsPanel.xaml" -] - -for page in pages: - page_element = ET.SubElement(item_group, "Page", Include=page) - ET.SubElement(page_element, "SubType").text = "Designer" - ET.SubElement(page_element, "Generator").text = "MSBuild:Compile" - -embedded_resources = [ - "Properties\\Resources.resx" -] - -for resource in embedded_resources: - embedded_resource = ET.SubElement(item_group, "EmbeddedResource", Include=resource) - ET.SubElement(embedded_resource, "Generator").text = "ResXFileCodeGenerator" - ET.SubElement(embedded_resource, "LastGenOutput").text = resource.replace(".resx", ".Designer.py") - -none_files = [ - "Properties\\Settings.settings", - "App.config" -] - -for none_file in none_files: - none = ET.SubElement(item_group, "None", Include=none_file) - if none_file.endswith(".settings"): - ET.SubElement(none, "Generator").text = "SettingsSingleFileGenerator" - ET.SubElement(none, "LastGenOutput").text = none_file.replace(".settings", ".Designer.py") - -ET.SubElement(item_group, "AppDesigner", Include="Properties\\") - -bootstrapper_packages = [ - (".NETFramework,Version=v4.5", "Microsoft .NET Framework 4.5 %28x86 and x64%29", "true"), - ("Microsoft.Net.Framework.3.5.SP1", ".NET Framework 3.5 SP1", "false") -] - -for package in bootstrapper_packages: - bootstrapper_package = ET.SubElement(item_group, "BootstrapperPackage", Include=package[0]) - ET.SubElement(bootstrapper_package, "Visible").text = "False" - ET.SubElement(bootstrapper_package, "ProductName").text = package[1] - ET.SubElement(bootstrapper_package, "Install").text = package[2] - -ET.SubElement(item_group, "Resource", Include="SANS_Icon.ico") - -ET.SubElement(item_group, "WCFMetadata", Include="Connected Services\\") - -ET.SubElement(root, "Import", Project="$(MSBuildToolsPath)\\Microsoft.CSharp.targets") - -tree = ET.ElementTree(root) -tree.write("SANS_Script_GUI_v4.xml", encoding="utf-8", xml_declaration=True) \ No newline at end of file diff --git a/SANS_Script_GUI_v4.csproj.user.py b/SANS_Script_GUI_v4.csproj.user.py deleted file mode 100644 index 13474b5..0000000 --- a/SANS_Script_GUI_v4.csproj.user.py +++ /dev/null @@ -1,16 +0,0 @@ -import xml.etree.ElementTree as ET - -root = ET.Element("Project", ToolsVersion="14.0", xmlns="http://schemas.microsoft.com/developer/msbuild/2003") - -property_group = ET.SubElement(root, "PropertyGroup") -ET.SubElement(property_group, "PublishUrlHistory").text = "publish\\" -ET.SubElement(property_group, "InstallUrlHistory") -ET.SubElement(property_group, "SupportUrlHistory") -ET.SubElement(property_group, "UpdateUrlHistory") -ET.SubElement(property_group, "BootstrapperUrlHistory") -ET.SubElement(property_group, "ErrorReportUrlHistory") -ET.SubElement(property_group, "FallbackCulture").text = "en-US" -ET.SubElement(property_group, "VerifyUploadedFiles").text = "false" - -tree = ET.ElementTree(root) -tree.write("SANS_Script_GUI_v4.xml", encoding="utf-8", xml_declaration=True) \ No newline at end of file diff --git a/SANS_Script_GUI_v4.xml b/SANS_Script_GUI_v4.xml deleted file mode 100644 index 5e50bb7..0000000 --- a/SANS_Script_GUI_v4.xml +++ /dev/null @@ -1,2 +0,0 @@ - -DebugAnyCPU{5DBFF2C3-05A0-4B4B-9080-8224D86A7B40}WinExePropertiesLOQ_Script_GuiSANS_Script_Gui_v4v4.8512{60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}4falsepublish\trueDiskfalseForeground7Daysfalsefalsetrue01.0.0.%2afalsetruex64truefullfalsebin\Debug\DEBUG;TRACEprompt4falsex64pdbonlytruebin\Release\TRACEprompt4SANS_Icon.icoTrue4.0Dialogs\PreviewDialog.xamlEstimationPanel.xamlSettingsPanel.xamlApp.xamlMainWindow.xamlTrueTrueProperties\Resources.resxTrueTrueProperties\Settings.resxTrueDesignerMSBuild:CompileDesignerMSBuild:CompileDesignerMSBuild:CompileDesignerMSBuild:CompileResXFileCodeGeneratorProperties\Resources.Designer.csSettingsSingleFileGeneratorProperties\Settings.Designer.csFalseMicrosoft .NET Framework 4.5 %28x86 and x64%29trueFalse.NET Framework 3.5 SP1false \ No newline at end of file diff --git a/SANS_Script_Writer/SANS_Script_Writer.sln b/SANS_Script_Writer/SANS_Script_Writer.sln deleted file mode 100644 index d5ff645..0000000 --- a/SANS_Script_Writer/SANS_Script_Writer.sln +++ /dev/null @@ -1,20 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 17 -VisualStudioVersion = 17.12.35527.113 d17.12 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{888888A0-9F3D-457C-B088-3A5042F75D52}") = "SANS_Script_Writer", "SANS_Script_Writer\SANS_Script_Writer.pyproj", "{83C517F7-94B7-490E-9812-1A281BC3AABB}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {83C517F7-94B7-490E-9812-1A281BC3AABB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {83C517F7-94B7-490E-9812-1A281BC3AABB}.Release|Any CPU.ActiveCfg = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection -EndGlobal diff --git a/SANS_Script_Writer/SANS_Script_Writer/SANS_Script_Writer.py b/SANS_Script_Writer/SANS_Script_Writer/SANS_Script_Writer.py deleted file mode 100644 index 8b13789..0000000 --- a/SANS_Script_Writer/SANS_Script_Writer/SANS_Script_Writer.py +++ /dev/null @@ -1 +0,0 @@ - diff --git a/SANS_Script_Writer/SANS_Script_Writer/SANS_Script_Writer.pyproj b/SANS_Script_Writer/SANS_Script_Writer/SANS_Script_Writer.pyproj deleted file mode 100644 index b77f107..0000000 --- a/SANS_Script_Writer/SANS_Script_Writer/SANS_Script_Writer.pyproj +++ /dev/null @@ -1,35 +0,0 @@ - - - Debug - 2.0 - 83c517f7-94b7-490e-9812-1a281bc3aabb - . - SANS_Script_Writer.py - - - . - . - SANS_Script_Writer - SANS_Script_Writer - - - true - false - - - true - false - - - - - - - - - - - - \ No newline at end of file diff --git a/Scripting/GeniePythonScriptGenerator.cs b/Scripting/GeniePythonScriptGenerator.cs deleted file mode 100644 index 3db9a71..0000000 --- a/Scripting/GeniePythonScriptGenerator.cs +++ /dev/null @@ -1,540 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Collections.ObjectModel; - -namespace LOQ_Script_Gui.Scripting -{ - class GeniePythonScriptGenerator : IScriptGenerator - { - string indent = " "; - - private bool isLarmor = false; - private bool isloq = false; - private bool issans2d = false; - private bool iszoom = false; - - public GeniePythonScriptGenerator(bool isLarmor, bool isloq, bool isZoom, bool isSans2d) - { - this.isLarmor = isLarmor; - this.isloq = isloq; - this.issans2d = isSans2d; - this.iszoom = isZoom; - } - - public string GenerateScript(ObservableCollection runs, ExperimentSettings settings, String scriptname) - { - string script = "# Script created by SANSScript at " + DateTime.Now + Environment.NewLine; - script += Environment.NewLine; - script += GenerateHeader(scriptname); - script += GenerateSetup(settings); - script += GenerateRuns(runs, settings); - script += Environment.NewLine; - - return script; - } - - private string GetIndentString(int n) - { - return new StringBuilder().Insert(0, indent, n).ToString(); - } - - private string GenerateHeader(String scriptname) - { - //string script = "import LSS.SANSroutines_v2 as lm" + Environment.NewLine ; - //string script = "from SansScripting import *" + Environment.NewLine; - //script += "@user_script" + Environment.NewLine + Environment.NewLine; - //script += Environment.NewLine; - string script = "import sys" + Environment.NewLine; - script += "from genie_python import genie as g, BLOCK_NAMES as b" + Environment.NewLine; - script += "sys.path.append(r\"c:\\instrument\\scripts\")" + Environment.NewLine; - - if (isLarmor) - { - script += "from instrument.larmor import * # pylint: disable=wildcard-import, unused-wildcard-import" + Environment.NewLine; - } - if (isloq) - { - script += "from instrument.loq import * # pylint: disable=wildcard-import, unused-wildcard-import" + Environment.NewLine; - } - if (iszoom) - { - script += "from instrument.zoom import * # pylint: disable=wildcard-import, unused-wildcard-import" + Environment.NewLine; - } - if (issans2d) - { - script += "from instrument.sans2d import * # pylint: disable=wildcard-import, unused-wildcard-import" + Environment.NewLine; - } - - script += Environment.NewLine; - script += "def "+scriptname+"():" + Environment.NewLine; - - return script; - } - - private string GenerateSetup(ExperimentSettings settings) - { - string script = ""; - - // TODO: Set number of available periods - - //script += GetIndentString(1) + "lm.setuplarmor_normal()" + Environment.NewLine; - script += GetIndentString(1) + string.Format("g.change_sample_par('Width', '{0}')", settings.SampleWidth) + Environment.NewLine; - script += GetIndentString(1) + string.Format("g.change_sample_par('height', '{0}')", settings.SampleHeight) + Environment.NewLine; - script += GetIndentString(1) + string.Format("g.change_sample_par('Geometry', '{0}')", settings.SampleGeometry) + Environment.NewLine + Environment.NewLine; - //script += GetIndentString(1) + string.Format("set_pv('IN:LARMOR:PARS:SAMPLE:WIDTH:SP', '{0}')", settings.SampleWidth) + Environment.NewLine; - //script += GetIndentString(1) + string.Format("set_pv('IN:LARMOR:PARS:SAMPLE:HEIGHT:SP', '{0}')", settings.SampleHeight) + Environment.NewLine; - //script += GetIndentString(1) + string.Format("set_pv('IN:LARMOR:PARS:SAMPLE:GEOMETRY:SP', '{0}')", settings.SampleGeometry) + Environment.NewLine + Environment.NewLine; - - return script; - } - - private string CheckPositions(ObservableCollection runs) - { - string script = ""; - - // Check that the positions are vailid - if (isLarmor) - { - string positionlist = "\""; - - foreach (Experiment exp in runs) - { - if (!string.IsNullOrWhiteSpace(exp.Position)) - { - positionlist += exp.Position + ","; - } - } - - if (positionlist.Length > 1) - { - if (positionlist.EndsWith(",")) - { - positionlist = positionlist.Substring(0, positionlist.Length - 1); - } - - positionlist += "\""; - - string tscript = string.Format("if check_move_pos([{0}]) != True", positionlist); - tscript = tscript.Replace(",", "\",\""); - script += GetIndentString(1) + tscript; - script += Environment.NewLine; - script += GetIndentString(2)+"print \"Invalid positions - script aborted\"" + Environment.NewLine; - script += GetIndentString(2)+"return" + Environment.NewLine + Environment.NewLine; - } - } - - //return script; - return ""; - } - private string GenerateRuns(ObservableCollection runs, ExperimentSettings settings) - { - string script = ""; - script = CheckPositions(runs); - - if (runs.Count == 0) - { - // Nothing to do - return script; - } - - if (settings.Order == RunOrder.AllTrans) - { - // All TRANS first - script += GetIndentString(1) + string.Format("for i in range({0}):", settings.NumTrans) + Environment.NewLine; - - foreach (Experiment exp in runs) - { - if (exp.Trans != 0) - { - int indent = 2; - script = DoTransRun(script, exp, indent, settings); - } - } - - // Then the SANS - script += GetIndentString(1) + string.Format("for i in range({0}):", settings.NumSans) + Environment.NewLine; - - foreach (Experiment exp in runs) - { - if (exp.Sans != 0) - { - int indent = 2; - script = DoSansRun(script, exp, indent, settings); - } - } - } - else if (settings.Order == RunOrder.AllSans) - { - // All SANS first - script += GetIndentString(1) + string.Format("for i in range({0}):", settings.NumSans) + Environment.NewLine; - - foreach (Experiment exp in runs) - { - if (exp.Sans != 0) - { - int indent = 2; - script = DoSansRun(script, exp, indent, settings); - } - } - - // Then the TRANS if we are running them - // check to see if there are actually any trans runs required by adding up all the trans count times - double sumTrans = 0.0; - foreach (Experiment exp in runs) - { - if (exp.Trans != 0) - { - sumTrans += exp.Trans; - } - } - - if (settings.NumTrans != 0 && sumTrans > 0.01) - { - script += GetIndentString(1) + string.Format("for i in range({0}):", settings.NumTrans) + Environment.NewLine; - } - foreach (Experiment exp in runs) - { - if (exp.Trans != 0) - { - int indent = 2; - script = DoTransRun(script, exp, indent, settings); - } - } - } - else if (settings.Order == RunOrder.TransFirst || settings.Order == RunOrder.SansFirst) - { - // TRANS - SANS - TRANS - SANS etc. or SANS - TRANS - SANS - TRANS etc - // Note: number of SANS and TRANS runs may be different - script += GetIndentString(1) + string.Format("num_sans = {0}", settings.NumSans) + Environment.NewLine; - script += GetIndentString(1) + string.Format("num_trans = {0}", settings.NumTrans) + Environment.NewLine; - script += GetIndentString(1) + "count = 0" + Environment.NewLine; - script += GetIndentString(1) + "while True:" + Environment.NewLine; - - int indent = 2; - - foreach (Experiment exp in runs) - { - if (settings.Order == RunOrder.TransFirst) - { - if (exp.Trans != 0) - { - script += GetIndentString(indent) + "if count < num_trans:" + Environment.NewLine; - script = DoTransRun(script, exp, indent + 1, settings); - } - if (exp.Sans != 0) - { - script += GetIndentString(indent) + "if count < num_sans:" + Environment.NewLine; - script = DoSansRun(script, exp, indent + 1, settings); - } - } - else - { - if (exp.Sans != 0) - { - script += GetIndentString(indent) + "if count < num_sans:" + Environment.NewLine; - script = DoSansRun(script, exp, indent + 1, settings); - } - if (exp.Trans != 0) - { - script += GetIndentString(indent) + "if count < num_trans:" + Environment.NewLine; - script = DoTransRun(script, exp, indent + 1, settings); - } - } - } - - script += GetIndentString(indent) + "count += 1" + Environment.NewLine; - script += GetIndentString(indent) + "if count >= num_trans and count >= num_sans: break" + Environment.NewLine; - } - - return script; - } - - private string DoTransRun(string script, Experiment exp, int indent, ExperimentSettings settings) - { - script += DoSampleEnvironment(exp, indent); - //script += SetAperture(settings.A2SettingTrans, indent); - - string waitfor = "uamps"; // Default to uamps - string rtype = "histogram"; // Default to event mode - string fulltitle = "{" + exp.Sample + "}"; - - if (settings.CollectionMode == DataCollection.Events) - { - rtype = "event"; - } - - if (exp.TransWait == WaitForUnits.Frames) - { - waitfor = "frames"; - } - else if (exp.TransWait == WaitForUnits.Seconds) - { - waitfor = "seconds"; - } - else if (exp.TransWait == WaitForUnits.Minutes) - { - waitfor = "minutes"; - } - - if (isLarmor == true || iszoom == true) - { - script += GetIndentString(indent) + string.Format("do_trans(title='{1}',pos='{0}', {2}={3}, thickness={4})", - exp.Position, fulltitle, waitfor, exp.Trans, exp.Thickness) + Environment.NewLine; - } - else if(isloq == true) - { - script += GetIndentString(indent) + string.Format("do_trans(title='{1}',pos='{0}', {2}={3}, thickness={4}, aperture='small')", - exp.Position, fulltitle, waitfor, exp.Trans, exp.Thickness) + Environment.NewLine; - } - else - { - script += GetIndentString(indent) + string.Format("do_trans(title='{1}',pos='{0}', {2}={3}, thickness={4}, dae='transmission', aperture='{5}')", - exp.Position, fulltitle, waitfor, exp.Trans, exp.Thickness,ApertureString(settings.A2SettingTrans)) + Environment.NewLine; - } - - script += GetIndentString(indent) + exp.PostCommand + Environment.NewLine; - return script; - } - - private string DoSansRun(string script, Experiment exp, int indent, ExperimentSettings settings) - { - script += DoSampleEnvironment(exp, indent); - //script += SetAperture(settings.A2SettingSans, indent); - - string waitfor = "uamps"; // Default to uamps - string rtype = "histogram"; // Default to histogram mode - string fulltitle = "{" + exp.Sample + "}"; - - if (!string.IsNullOrWhiteSpace(exp.Background)) - { - fulltitle += "_{" + exp.Background + "}"; - } - - if (settings.CollectionMode == DataCollection.Events || settings.CollectionMode == DataCollection.TRANSSANS_Events) - { - rtype = "event"; - } - else if (settings.CollectionMode == DataCollection.SESANS) - { - rtype = "sesans"; - } - if (settings.CollectionMode == DataCollection.TRANSSANS_Events || settings.CollectionMode == DataCollection.TRANSSANS_Histogram) - { - // add TRANS to the title so that when SANS is added by do_sans we get _TRANS_SANS - fulltitle += "_TRANS"; - } - - if (exp.SansWait == WaitForUnits.Frames) - { - waitfor = "frames"; - } - else if (exp.SansWait == WaitForUnits.Seconds) - { - waitfor = "seconds"; - } - else if (exp.SansWait == WaitForUnits.Minutes) - { - waitfor = "minutes"; - } - - if (isLarmor == true || iszoom == true) - { - script += GetIndentString(indent) + string.Format("do_sans(title='{1}',pos='{0}', {2}={3}, thickness={4}, dae='{5}')", - exp.Position, fulltitle, waitfor, exp.Sans, exp.Thickness, rtype) + Environment.NewLine; - } - else - { - script += GetIndentString(indent) + string.Format("do_sans(title='{1}',pos='{0}', {2}={3}, thickness={4}, dae='{5}',aperture='{6}')", - exp.Position, fulltitle, waitfor, exp.Sans, exp.Thickness, rtype,ApertureString(settings.A2SettingSans)) + Environment.NewLine; - } - - script += GetIndentString(indent) + exp.PostCommand + Environment.NewLine; - return script; - } - - private string SetAperture(string size, int indent) - { - string script = ""; - if (size == A2Setting.Large || size == A2Setting.Larmor) - { - script += GetIndentString(indent) + "set_aperture('large')" + Environment.NewLine; - } - else if (size == A2Setting.Medium || size == A2Setting.Larmor) - { - script += GetIndentString(indent) + "set_aperture('medium')" + Environment.NewLine; - } - else if (size == A2Setting.Small || size == A2Setting.Larmor) - { - script += GetIndentString(indent) + "set_aperture('small')" + Environment.NewLine; - } - else if (size == A2Setting.XLarge || size == A2Setting.Larmor) - { - script += GetIndentString(indent) + "set_aperture('xlarge')" + Environment.NewLine; - } - return script; - } - - private string ApertureString(string size) - { - string script = ""; - if (size == A2Setting.Large || size == A2Setting.Larmor) - { - script = "large"; - } - else if (size == A2Setting.Medium || size == A2Setting.Larmor) - { - script = "medium"; - } - else if (size == A2Setting.Small || size == A2Setting.Larmor) - { - script = "small"; - } - else if (size == A2Setting.XLarge || size == A2Setting.Larmor) - { - script = "xlarge"; - } - return script; - } - - private string DoSampleEnvironment(Experiment exp, int indent) - { - string script = ""; - - // Set Temps first - if (!string.IsNullOrWhiteSpace(exp.Temperature1)) - { - double temp1 = Convert.ToDouble(exp.Temperature1); - double temp1low = temp1-1.0; - double temp1high = temp1 + 1.0; - if (isloq == true) - { - script += GetIndentString(indent) + string.Format("g.cset(internal_setpoint_1={0})", exp.Temperature1) + Environment.NewLine; - script += GetIndentString(indent) + string.Format("g.cset(\"Julabo_1_ext\",lowlimit={0},highlimit={1},runcontrol=True)", Convert.ToString(temp1low), Convert.ToString(temp1high)) + Environment.NewLine; - }else - { - script += GetIndentString(indent) + string.Format("g.cset(JULABO1_SP={0})", exp.Temperature1) + Environment.NewLine; - script += GetIndentString(indent) + string.Format("g.cset(\"JULABO1_TEMP\",lowlimit={0},highlimit={1},runcontrol=True)", Convert.ToString(temp1low), Convert.ToString(temp1high)) + Environment.NewLine; - } - } - - if (!string.IsNullOrWhiteSpace(exp.Temperature2)) - { - double temp2 = Convert.ToDouble(exp.Temperature2); - double temp2low = temp2 - 1.0; - double temp2high = temp2 + 1.0; - if (isloq == true) - { - script += GetIndentString(indent) + string.Format("g.cset(internal_setpoint_2={0})", exp.Temperature2) + Environment.NewLine; - script += GetIndentString(indent) + string.Format("g.cset(\"Julabo_2_ext\",lowlimit={0},highlimit={1},runcontrol=True)", Convert.ToString(temp2low), Convert.ToString(temp2high)) + Environment.NewLine; - } - else - { - script += GetIndentString(indent) + string.Format("g.cset(JULABO2_SP={0})", exp.Temperature2) + Environment.NewLine; - script += GetIndentString(indent) + string.Format("g.cset(\"JULABO2_TEMP\",lowlimit={0},highlimit={1},runcontrol=True)", Convert.ToString(temp2low), Convert.ToString(temp2high)) + Environment.NewLine; - } - } - - // Check for pre-command - always run immediately after the temp is set - if (!string.IsNullOrWhiteSpace(exp.PreCommand)) - { - script += GetIndentString(indent) + exp.PreCommand + Environment.NewLine; - } - - if (!string.IsNullOrWhiteSpace(exp.Field)) - { - script += GetIndentString(indent) + string.Format("g.cset(FIELD={0})", exp.Field) + Environment.NewLine; - } - - if (!string.IsNullOrWhiteSpace(exp.ShearRate1)) - { - script += GetIndentString(indent) + string.Format("g.cset(SHEAR_RATE_1={0})", exp.ShearRate1) + Environment.NewLine; - } - - if (!string.IsNullOrWhiteSpace(exp.ShearRate2)) - { - script += GetIndentString(indent) + string.Format("g.cset(SHEAR_RATE_2={0})", exp.ShearRate2) + Environment.NewLine; - } - - if (!string.IsNullOrWhiteSpace(exp.ShearAngle1)) - { - script += GetIndentString(indent) + string.Format("g.cset(SHEAR_ANGLE_1={0})", exp.ShearAngle1) + Environment.NewLine; - } - - if (!string.IsNullOrWhiteSpace(exp.ShearAngle2)) - { - script += GetIndentString(indent) + string.Format("g.cset(SHEAR_ANGLE_2={0})", exp.ShearAngle1) + Environment.NewLine; - } - - return script; - } - - public string EstimateRunTime(ObservableCollection runs, ExperimentSettings settings, Estimation estimation) - { - double minutes = 0; - - if (settings.NumSans > 0) - { - minutes += CalculateTime(runs, true, estimation.CountRate, estimation.MoveTime) * settings.NumSans; - } - - if (settings.NumTrans > 0) - { - minutes += CalculateTime(runs, false, estimation.CountRate, estimation.MoveTime) * settings.NumTrans; - } - - DateTime time = new DateTime(1, 1, 1, 0, 0, 0); - - time = time.AddMinutes(minutes); - - return time.ToLongTimeString(); - } - - private static double CalculateTime(ObservableCollection runs, bool isSans, double countRate, double secondsBetween) - { - double minutes = 0; - - foreach (Experiment exp in runs) - { - if (!string.IsNullOrWhiteSpace(exp.Position)) - { - if (isSans) - { - minutes += GetMinutes(exp.SansWait, exp.Sans, countRate); - } - else - { - minutes += GetMinutes(exp.TransWait, exp.Trans, countRate); - } - - minutes += secondsBetween / 60; - } - } - - return minutes; - } - - private static double GetMinutes(string wait, double length, double countRate) - { - if (wait == WaitForUnits.Minutes) - { - return length; - } - else if (wait == WaitForUnits.Seconds) - { - return length / 60.0; - } - else if (wait == WaitForUnits.Frames) - { - return length / (25.0 * 60.0); - } - else - { - // If in doubt use micro-amps - return length / (countRate / 60.0); - } - } - } -} diff --git a/Scripting/IScriptGenerator.cs b/Scripting/IScriptGenerator.cs deleted file mode 100644 index fdddd91..0000000 --- a/Scripting/IScriptGenerator.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System; -using System.Collections.ObjectModel; - -namespace LOQ_Script_Gui.Scripting -{ - interface IScriptGenerator - { - string GenerateScript(ObservableCollection runs, ExperimentSettings settings, String scriptname); - string EstimateRunTime(ObservableCollection runs, ExperimentSettings settings, Estimation estimation); - } -} diff --git a/Scripting/OpenGenieScriptGenerator.cs b/Scripting/OpenGenieScriptGenerator.cs deleted file mode 100644 index 9c9f5b7..0000000 --- a/Scripting/OpenGenieScriptGenerator.cs +++ /dev/null @@ -1,627 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Collections.ObjectModel; - -namespace LOQ_Script_Gui.Scripting -{ - class OpenGenieScriptGenerator : IScriptGenerator - { - private bool isSans2d = false; - - public OpenGenieScriptGenerator(bool isSans2d) - { - this.isSans2d = isSans2d; - } - - public string GenerateScript(ObservableCollection runs, ExperimentSettings settings, String scriptname) - { - string script = "# Script created by LOQ Script at " + DateTime.Now + Environment.NewLine; - script += "# Script MUST be loaded in to Open GENIE using the loadscript command." + Environment.NewLine + Environment.NewLine; - - if (isSans2d) - { - script += "QUALIFIERS /test" + Environment.NewLine; - } - - script += string.Format("LOCAL transCount = 0 sansCount = 0 numTRANS={0} numSANS={1}", settings.NumTrans, settings.NumSans); - script += Environment.NewLine + Environment.NewLine; - - script += "SETSCRIPTNAME THIS_PROCEDURE()" + Environment.NewLine + Environment.NewLine; - - script += string.Format("Sample_par width={0} height={1} geometry=\"{2}\"", settings.SampleWidth, settings.SampleHeight, settings.SampleGeometry); - script += Environment.NewLine + Environment.NewLine; - - if (isSans2d) - { - string positionlist = "\""; - - foreach (Experiment exp in runs) - { - if (!string.IsNullOrWhiteSpace(exp.Position)) - { - positionlist += exp.Position + ","; - } - } - - if (positionlist.Length > 1) - { - if (positionlist.EndsWith(",")) - { - positionlist = positionlist.Substring(0, positionlist.Length - 1); - } - - positionlist += "\""; - - script += string.Format("IF NOT CHECK_MOVE_POS({0})", positionlist); - script += Environment.NewLine; - script += " PRINTEN \"Invalid positions - script aborted\"" + Environment.NewLine; - script += " RETURN" + Environment.NewLine; - script += "ENDIF" + Environment.NewLine + Environment.NewLine; - - } - - script += "IF test" + Environment.NewLine; - script += " RETURN" + Environment.NewLine; - script += "ENDIF" + Environment.NewLine + Environment.NewLine; - } - - if (settings.Order == RunOrder.AllTrans) - { - // Do all trans first - if (settings.LoopPerRun) - { - script += DoAllTransLoopEach(runs, settings, true, true); - script += DoAllSansLoopEach(runs, settings, true, true); - } - else - { - script += "LOOP" + Environment.NewLine + Environment.NewLine; - script += DoAllTrans(runs, settings, true, true); - script += DoAllSans(runs, settings, true, true); - script += "EXITIF ((transCount >= numTRANS) AND (sansCount >= numSANS))" + Environment.NewLine + Environment.NewLine; - script += "ENDLOOP" + Environment.NewLine + Environment.NewLine; - } - } - else if (settings.Order == RunOrder.TransFirst) - { - // Do Trans-Sans-Trans-Sans... - if (settings.LoopPerRun) - { - foreach (Experiment exp in runs) - { - script += DoSingleTransLoopEach(exp, settings, true, false); - script += DoSingleSansLoopEach(exp, settings, false, true); - } - } - else - { - script += "LOOP" + Environment.NewLine + Environment.NewLine; - - foreach (Experiment exp in runs) - { - script += DoSingleTrans(exp, settings, true, false); - script += DoSingleSans(exp, settings, false, true); - } - - script += "transCount = transCount + 1" + Environment.NewLine; - script += "sansCount = sansCount + 1" + Environment.NewLine + Environment.NewLine; - script += "EXITIF ((transCount >= numTRANS) AND (sansCount >= numSANS))" + Environment.NewLine + Environment.NewLine; - script += "ENDLOOP" + Environment.NewLine + Environment.NewLine; - } - } - else if (settings.Order == RunOrder.AllSans) - { - // Do All Sans first - if (settings.LoopPerRun) - { - script += DoAllSansLoopEach(runs, settings, true, true); - script += DoAllTransLoopEach(runs, settings, true, true); - } - else - { - script += "LOOP" + Environment.NewLine + Environment.NewLine; - script += DoAllSans(runs, settings, true, true); - script += DoAllTrans(runs, settings, true, true); - script += "EXITIF ((transCount >= numTRANS) AND (sansCount >= numSANS))" + Environment.NewLine + Environment.NewLine; - script += "ENDLOOP" + Environment.NewLine + Environment.NewLine; - } - } - else if (settings.Order == RunOrder.SansFirst) - { - // Do Sans-Trans-Sans-Trans... - if (settings.LoopPerRun) - { - foreach (Experiment exp in runs) - { - script += DoSingleSansLoopEach(exp, settings, true, false); - script += DoSingleTransLoopEach(exp, settings, false, true); - } - } - else - { - script += "LOOP" + Environment.NewLine + Environment.NewLine; - - foreach (Experiment exp in runs) - { - script += DoSingleSans(exp, settings, true, false); - script += DoSingleTrans(exp, settings, false, true); - } - - script += "transCount = transCount + 1" + Environment.NewLine; - script += "sansCount = sansCount + 1" + Environment.NewLine + Environment.NewLine; - script += "EXITIF ((transCount >= numTRANS) AND (sansCount >= numSANS))" + Environment.NewLine + Environment.NewLine; - script += "ENDLOOP" + Environment.NewLine + Environment.NewLine; - } - } - - //script += "ENDPROCEDURE" + Environment.NewLine; - script += "PRINTN \"SCRIPT COMPLETED \"" + Environment.NewLine + Environment.NewLine; - - return script; - } - - private static string DoSingleSans(Experiment exp, ExperimentSettings settings, bool doPreCom, bool doPostCom) - { - string script = ""; - - if (settings.NumSans > 0) - { - if (exp.Sans > 0) - { - script += "IF (sansCount < numSANS)" + Environment.NewLine; - script += setSansAperture(settings.A2SettingSans); - script += DoSingleRun(false, exp, false, doPreCom, doPostCom, settings); - script += Environment.NewLine + "ENDIF" + Environment.NewLine + Environment.NewLine; - } - } - return script; - } - - private static string DoSingleTrans(Experiment exp, ExperimentSettings settings, bool doPreCom, bool doPostCom) - { - string script = ""; - - if (settings.NumTrans > 0) - { - if (exp.Trans > 0) - { - script += "IF (transCount < numTRANS)" + Environment.NewLine; - script += setTransAperture(settings.A2SettingTrans); - script += DoSingleRun(true, exp, false, doPreCom, doPostCom, settings); - script += Environment.NewLine + "ENDIF" + Environment.NewLine + Environment.NewLine; - } - } - return script; - } - - private static string DoSingleSansLoopEach(Experiment exp, ExperimentSettings settings, bool doPreCom, bool doPostCom) - { - string script = ""; - - if (settings.NumSans > 0) - { - if (exp.Sans > 0) - { - script += setSansAperture(settings.A2SettingSans); - script += DoSingleRun(false, exp, true, doPreCom, doPostCom, settings) + Environment.NewLine; - } - } - return script; - } - - private static string DoSingleTransLoopEach(Experiment exp, ExperimentSettings settings, bool doPreCom, bool doPostCom) - { - string script = ""; - - if (settings.NumTrans > 0) - { - if (exp.Trans > 0) - { - script += setTransAperture(settings.A2SettingTrans); - script += DoSingleRun(true, exp, true, doPreCom, doPostCom,settings) + Environment.NewLine; - } - } - return script; - } - - private static string DoAllSans(ObservableCollection runs, ExperimentSettings settings, bool doPreCom, bool doPostCom) - { - string script = ""; - - if (settings.NumSans > 0) - { - script += setSansAperture(settings.A2SettingSans); - - script += "IF sansCount < numSANS" + Environment.NewLine + Environment.NewLine; - - // Loop through each experiment - foreach (Experiment exp in runs) - { - script += DoSingleRun(false, exp, false, doPreCom, doPostCom, settings) + Environment.NewLine; - } - - script += "sansCount = sansCount + 1" + Environment.NewLine + Environment.NewLine; - script += "ENDIF" + Environment.NewLine + Environment.NewLine; - } - - return script; - } - - private static string DoAllTrans(ObservableCollection runs, ExperimentSettings settings, bool doPreCom, bool doPostCom) - { - string script = ""; - - if (settings.NumTrans > 0) - { - script += setTransAperture(settings.A2SettingTrans); - - script += "IF transCount < numTRANS" + Environment.NewLine + Environment.NewLine; - - // Loop through each experiment - foreach (Experiment exp in runs) - { - script += DoSingleRun(true, exp, false, doPreCom, doPostCom, settings) + Environment.NewLine; - } - - script += "transCount = transCount + 1" + Environment.NewLine + Environment.NewLine; - script += "ENDIF" + Environment.NewLine + Environment.NewLine; - } - - return script; - } - - private static string DoAllSansLoopEach(ObservableCollection runs, ExperimentSettings settings, bool doPreCom, bool doPostCom) - { - string script = ""; - - if (settings.NumSans > 0) - { - script += setSansAperture(settings.A2SettingSans); - - // Loop through each experiment - foreach (Experiment exp in runs) - { - script += DoSingleRun(false, exp, true, doPreCom, doPostCom, settings) + Environment.NewLine; - } - } - - return script; - } - - private static string DoAllTransLoopEach(ObservableCollection runs, ExperimentSettings settings, bool doPreCom, bool doPostCom) - { - string script = ""; - - if (settings.NumTrans > 0) - { - script += setTransAperture(settings.A2SettingTrans); - - // Loop through each experiment - foreach (Experiment exp in runs) - { - script += DoSingleRun(true, exp, true, doPreCom, doPostCom, settings) + Environment.NewLine; - } - } - - return script; - } - - private static string setSansAperture(string A2SizeSans) - { - string script = ""; - - if (A2SizeSans == A2Setting.Large) - { - script += "DO_SANS /LARGE" + Environment.NewLine + Environment.NewLine; - } - else if (A2SizeSans == A2Setting.Medium) - { - script += "DO_SANS /MEDIUM" + Environment.NewLine + Environment.NewLine; - } - else if (A2SizeSans == A2Setting.Small) - { - script += "DO_SANS /SMALL" + Environment.NewLine + Environment.NewLine; - } - else if (A2SizeSans == A2Setting.Trans) - { - script += "DO_SANS /TRANS" + Environment.NewLine + Environment.NewLine; - } - - return script; - } - - private static string setTransAperture(string A2SizeTrans) - { - string script = ""; - - if (string.IsNullOrWhiteSpace(A2SizeTrans)) - { - script += "DO_TRANS" + Environment.NewLine + Environment.NewLine; - } - else - { - if (A2SizeTrans == A2Setting.Large) - { - script += "DO_TRANS /LARGE" + Environment.NewLine + Environment.NewLine; - } - else if (A2SizeTrans == A2Setting.Medium) - { - script += "DO_TRANS /MEDIUM" + Environment.NewLine + Environment.NewLine; - } - else if (A2SizeTrans == A2Setting.Small) - { - script += "DO_TRANS /SMALL" + Environment.NewLine + Environment.NewLine; - } - else if (A2SizeTrans == A2Setting.Trans) - { - script += "DO_TRANS /TRANS" + Environment.NewLine + Environment.NewLine; - } - } - - return script; - } - - private static string DoSingleRun(bool isTrans, Experiment exp, bool isLoop, bool doPreCom, bool doPostCom, ExperimentSettings settings) - { - string script = ""; - - if (exp.Trans > 0 || exp.Sans > 0) - { - if (!string.IsNullOrWhiteSpace(exp.RbNumber)) - { - script += "change rbno=" + exp.RbNumber + Environment.NewLine; - } - - if (!string.IsNullOrWhiteSpace(exp.Position)) - { - if (doPreCom) - { - script += DoSampleEnvironment(exp); - } - - if (isLoop) - { - if (isTrans) - { - script += "LOOP transCount FROM 1 TO numTRANS" + Environment.NewLine; - } - else - { - script += "LOOP sansCount FROM 1 TO numSANS" + Environment.NewLine; - } - - script += CreateMoveCommand(isTrans, exp, settings); - script += "ENDLOOP" + Environment.NewLine; - } - else - { - script += CreateMoveCommand(isTrans, exp, settings); - } - - // Check for post-command - always run last - if (doPostCom && !string.IsNullOrWhiteSpace(exp.PostCommand)) - { - script += exp.PostCommand + Environment.NewLine; - } - } - } - - return script; - } - - private static string DoSampleEnvironment(Experiment exp) - { - string script = ""; - - // Set Temps first - if (!string.IsNullOrWhiteSpace(exp.Temperature1)) - { - script += "CSET TEMP=" + exp.Temperature1 + Environment.NewLine; - } - - if (!string.IsNullOrWhiteSpace(exp.Temperature2)) - { - script += "CSET TEMP2=" + exp.Temperature2 + Environment.NewLine; - } - - // Check for pre-command - always run immediately after the temp is set - if (!string.IsNullOrWhiteSpace(exp.PreCommand)) - { - script += exp.PreCommand + Environment.NewLine; - } - - if (!string.IsNullOrWhiteSpace(exp.Field)) - { - script += "CSET FIELD=" + exp.Field + Environment.NewLine; - } - - if (!string.IsNullOrWhiteSpace(exp.ShearRate1)) - { - script += "CSET SHEAR_RATE_1=" + exp.ShearRate1 + Environment.NewLine; - } - - if (!string.IsNullOrWhiteSpace(exp.ShearRate2)) - { - script += "CSET SHEAR_RATE_2=" + exp.ShearRate2 + Environment.NewLine; - } - - if (!string.IsNullOrWhiteSpace(exp.ShearAngle1)) - { - script += "CSET SHEAR_ANGLE_1=" + exp.ShearAngle1 + Environment.NewLine; - } - - if (!string.IsNullOrWhiteSpace(exp.ShearAngle2)) - { - script += "CSET SHEAR_ANGLE_2=" + exp.ShearAngle2 + Environment.NewLine; - } - - return script; - } - - private static string CreateMoveCommand(bool isTrans, Experiment exp, ExperimentSettings settings) - { - string script = "MOVE pos=\""+exp.Position+"\""; - string fulltitle = ""; - - - if (!string.IsNullOrWhiteSpace(exp.Sample)) - { - fulltitle = "{" + exp.Sample + "}"; - } - - if (!string.IsNullOrWhiteSpace(exp.Background)) - { - fulltitle += "_{"+exp.Background+"}"; - } - - if (!string.IsNullOrWhiteSpace(exp.Thickness)) - { - script += " thick=" + exp.Thickness; - } - else - { - script += " thick=1"; - } - - if (!string.IsNullOrWhiteSpace(exp.Period)) - { - script += " period=" + exp.Period; - } - - if (isTrans) - { - script += WriteWaitFor(exp.TransWait, exp.Trans); - - if (!string.IsNullOrWhiteSpace(fulltitle)) - { - script += string.Format(" title=\"{0}_TRANS\"", "{"+exp.Sample+"}"); - } - } - else - { - script += WriteWaitFor(exp.SansWait, exp.Sans); - - if (!string.IsNullOrWhiteSpace(fulltitle)) - { - if (settings.CollectionMode == DataCollection.TRANSSANS_Events || settings.CollectionMode == DataCollection.TRANSSANS_Histogram) - { - script += string.Format(" title=\"{0}_TRANS_SANS\"", fulltitle); - } - else - { - script += string.Format(" title=\"{0}_SANS\"", fulltitle); - } - } - } - - return script + Environment.NewLine; - - } - - private static string WriteWaitFor(string waitfor, double length) - { - string script = ""; - - if (!string.IsNullOrWhiteSpace(waitfor)) - { - if (waitfor.ToLower() == WaitForUnits.MicroAmps.ToLower()) - { - script += " uAhr=" + length; - } - else if (waitfor.ToLower() == WaitForUnits.Minutes.ToLower()) - { - script += " min=" + length; - } - else if (waitfor.ToLower() == WaitForUnits.Seconds.ToLower()) - { - script += " seconds=" + length; - } - else if (waitfor.ToLower() == WaitForUnits.Frames.ToLower()) - { - script += " frame=" + length; - } - else - { - // If in doubt use micro-amps - script += " uAhr=" + length; - } - } - else - { - // If in doubt use micro-amps - script += " uAhr=" + length; - } - return script; - } - - public string EstimateRunTime(ObservableCollection runs, ExperimentSettings settings, Estimation estimation) - { - double minutes = 0; - - if (settings.NumSans > 0) - { - minutes += CalculateTime(runs, true, estimation.CountRate, estimation.MoveTime) * settings.NumSans; - } - - if (settings.NumTrans > 0) - { - minutes += CalculateTime(runs, false, estimation.CountRate, estimation.MoveTime) * settings.NumTrans; - } - - DateTime time = new DateTime(1, 1, 1, 0, 0, 0); - - time = time.AddMinutes(minutes); - - return time.ToLongTimeString(); - } - - private static double CalculateTime(ObservableCollection runs, bool isSans, double countRate, double secondsBetween) - { - double minutes = 0; - - foreach (Experiment exp in runs) - { - if (!string.IsNullOrWhiteSpace(exp.Position)) - { - if (isSans) - { - minutes += GetMinutes(exp.SansWait, exp.Sans, countRate); - } - else - { - minutes += GetMinutes(exp.TransWait, exp.Trans, countRate); - } - - minutes += secondsBetween / 60; - } - } - - return minutes; - } - - private static double GetMinutes(string wait, double length, double countRate) - { - if (wait == WaitForUnits.Minutes) - { - return length; - } - else if (wait == WaitForUnits.Seconds) - { - return length / 60.0; - } - else if (wait == WaitForUnits.Frames) - { - return length / (25.0 * 60.0); - } - else - { - // If in doubt use micro-amps - return length / (countRate / 60.0); - } - } - - } -} diff --git a/SettingsPanel.xaml b/SettingsPanel.xaml deleted file mode 100644 index 8c63f8f..0000000 --- a/SettingsPanel.xaml +++ /dev/null @@ -1,72 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Loop over each run? - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/SettingsPanel.xaml.cs b/SettingsPanel.xaml.cs deleted file mode 100644 index 3e768e6..0000000 --- a/SettingsPanel.xaml.cs +++ /dev/null @@ -1,47 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Windows; -using System.Windows.Controls; -using System.Windows.Data; -using System.Windows.Documents; -using System.Windows.Input; -using System.Windows.Media; -using System.Windows.Media.Imaging; -using System.Windows.Navigation; -using System.Windows.Shapes; - -namespace LOQ_Script_Gui -{ - /// - /// Interaction logic for SettingsPanel.xaml - /// - public partial class SettingsPanel : UserControl - { - public event EventHandler OnPreviewClicked; - public event EventHandler OnWriteClicked; - - public SettingsPanel() - { - InitializeComponent(); - } - - private void btnPreviewScript_Click(object sender, RoutedEventArgs e) - { - if (OnPreviewClicked != null) - { - OnPreviewClicked(sender, e); - } - } - - private void btnWriteScript_Click(object sender, RoutedEventArgs e) - { - if (OnWriteClicked != null) - { - OnWriteClicked(sender, e); - } - } - } -} diff --git a/ViewModels/DataGridVM.cs b/ViewModels/DataGridVM.cs deleted file mode 100644 index 7dd4481..0000000 --- a/ViewModels/DataGridVM.cs +++ /dev/null @@ -1,41 +0,0 @@ -using System.Collections.ObjectModel; -using System.ComponentModel; - -namespace LOQ_Script_Gui -{ - class DataGridVM : INotifyPropertyChanged - { - public event PropertyChangedEventHandler PropertyChanged; - - protected void OnPropertyChanged(string name) - { - PropertyChangedEventHandler handler = PropertyChanged; - if (handler != null) - { - handler(this, new PropertyChangedEventArgs(name)); - } - } - - ObservableCollection runs = new ObservableCollection(); - public ObservableCollection Runs - { - get - { - return runs; - } - set - { - runs = value; - OnPropertyChanged("Runs"); - } - } - - public static ObservableCollection WaitForChoices - { - get - { - return WaitForUnits.Choices; - } - } - } -} diff --git a/ViewModels/EstimationPanelVM.cs b/ViewModels/EstimationPanelVM.cs deleted file mode 100644 index 441ae08..0000000 --- a/ViewModels/EstimationPanelVM.cs +++ /dev/null @@ -1,24 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Collections.ObjectModel; -using System.ComponentModel; - -namespace LOQ_Script_Gui -{ - class EstimationPanelVM : INotifyPropertyChanged - { - public event PropertyChangedEventHandler PropertyChanged; - - protected void OnPropertyChanged(string name) - { - PropertyChangedEventHandler handler = PropertyChanged; - if (handler != null) - { - handler(this, new PropertyChangedEventArgs(name)); - } - } - } -} diff --git a/ViewModels/InstrumentVM.cs b/ViewModels/InstrumentVM.cs deleted file mode 100644 index 84daf86..0000000 --- a/ViewModels/InstrumentVM.cs +++ /dev/null @@ -1,179 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Collections.ObjectModel; -using System.ComponentModel; - -namespace LOQ_Script_Gui -{ - class InstrumentVM : INotifyPropertyChanged - { - List loqApertureSizes = new List() - { - A2Setting.Large, A2Setting.Medium, A2Setting.Small - }; - - List sans2dApertureSizes = new List() - { - A2Setting.XLarge, A2Setting.Large, A2Setting.Medium, A2Setting.Small - }; - - List larmorApertureSizes = new List() - { - A2Setting.Larmor - //A2Setting.LarmorLarge, A2Setting.LarmorMedium, A2Setting.LarmorSmall - }; - - List zoomApertureSizes = new List() - { - A2Setting.Large, A2Setting.Medium, A2Setting.Small - }; - - public event PropertyChangedEventHandler PropertyChanged; - - protected void OnPropertyChanged(string name) - { - PropertyChangedEventHandler handler = PropertyChanged; - if (handler != null) - { - handler(this, new PropertyChangedEventArgs(name)); - } - } - - private bool isLarmor = true; - public bool IsLarmor - { - get - { - return isLarmor; - } - set - { - isLarmor = value; - if (value) - { - IsLoq = false; - IsSans2d = false; - IsZoom = false; - InstrumentInfo.ScriptGenerator = new Scripting.GeniePythonScriptGenerator(true,false,false,false); - InstrumentInfo.A2Label = "A1, S1 Setting:"; - InstrumentInfo.A2Choices = new ObservableCollection(larmorApertureSizes); - InstrumentInfo.A2TransEnabled = true; - InstrumentInfo.CollectionModeEnabled = true; - estimation.CountRate = 40; - //settings.A2SettingSans = A2Setting.LarmorMedium; - settings.A2SettingSans = Properties.Settings.Default.A2SettingSans; - //settings.A2SettingTrans = A2Setting.LarmorMedium; - settings.A2SettingTrans = Properties.Settings.Default.A2SettingTrans; - } - - OnPropertyChanged("IsLarmor"); - } - } - - private bool isLoq = false; - public bool IsLoq - { - get - { - return isLoq; - } - set - { - isLoq = value; - if (value) - { - IsLarmor = false; - IsSans2d = false; - IsZoom = false; - InstrumentInfo.ScriptGenerator = new Scripting.GeniePythonScriptGenerator(false,true,false,false); - InstrumentInfo.A2Label = "A2 Setting:"; - InstrumentInfo.A2Choices = new ObservableCollection(loqApertureSizes); - InstrumentInfo.A2TransEnabled = true; - InstrumentInfo.CollectionModeEnabled = true; - estimation.CountRate = 170; - //settings.A2SettingSans = A2Setting.Large; - settings.A2SettingSans = Properties.Settings.Default.A2SettingSans; - //settings.A2SettingTrans = A2Setting.Large; - settings.A2SettingTrans = Properties.Settings.Default.A2SettingTrans; - } - - OnPropertyChanged("IsLoq"); - } - } - - private bool isSans2d = false; - public bool IsSans2d - { - get - { - return isSans2d; - } - set - { - isSans2d = value; - if (value) - { - IsLarmor = false; - IsLoq = false; - IsZoom = false; - //InstrumentInfo.ScriptGenerator = new Scripting.OpenGenieScriptGenerator(true); - InstrumentInfo.ScriptGenerator = new Scripting.GeniePythonScriptGenerator(false, false, false, true); - InstrumentInfo.A2Label = "A6 Setting:"; - InstrumentInfo.A2Choices = new ObservableCollection(sans2dApertureSizes); - InstrumentInfo.A2TransEnabled = true; - InstrumentInfo.CollectionModeEnabled = true; - estimation.CountRate = 40; - //settings.A2SettingSans = A2Setting.Large; - settings.A2SettingSans = Properties.Settings.Default.A2SettingSans; - //settings.A2SettingTrans = A2Setting.Large; - settings.A2SettingTrans = Properties.Settings.Default.A2SettingTrans; - } - - OnPropertyChanged("IsSans2d"); - } - } - - private bool isZoom = false; - public bool IsZoom - { - get - { - return isZoom; - } - set - { - isZoom = value; - if (value) - { - IsLarmor = false; - IsLoq = false; - IsSans2d = false; - InstrumentInfo.ScriptGenerator = new Scripting.GeniePythonScriptGenerator(false,false,true,false); - InstrumentInfo.A2Label = "A6 Setting:"; - InstrumentInfo.A2Choices = new ObservableCollection(zoomApertureSizes); - InstrumentInfo.A2TransEnabled = true; - InstrumentInfo.CollectionModeEnabled = true; - estimation.CountRate = 40; - //settings.A2SettingSans = A2Setting.Large; - settings.A2SettingSans = Properties.Settings.Default.A2SettingSans; - //settings.A2SettingTrans = A2Setting.Large; - settings.A2SettingTrans = Properties.Settings.Default.A2SettingTrans; - } - - OnPropertyChanged("IsZoom"); - } - } - - ExperimentSettings settings; - Estimation estimation; - - public InstrumentVM(ExperimentSettings settings, Estimation estimation) - { - this.settings = settings; - this.estimation = estimation; - } - } -} diff --git a/ViewModels/InstrumentVM.py b/ViewModels/InstrumentVM.py deleted file mode 100644 index 5231a96..0000000 --- a/ViewModels/InstrumentVM.py +++ /dev/null @@ -1,36 +0,0 @@ -import tkinter as tk -from tkinter import StringVar, BooleanVar -from Models.A2Setting import A2Setting -from Models.DataCollection import DataCollection -from Models.InstrumentInfo import InstrumentInfo -from Models.Estimation import Estimation -from Models.ExperimentSettings import ExperimentSettings -from Scripting.ScriptGenerator import ScriptGenerator -from Properties.Settings import Settings - - -class InstrumentVM: - def __init__(self, experimentsettings=None, estimation=None): - #def __init__(self, experiment, estimate): - self.settings = experimentsettings if experimentsettings is not None else ExperimentSettings() - self.estimation = estimation if estimation is not None else Estimation() - - - - """ - self.is_larmor = False - self.is_loq = False - self.is_sans2d = False - self.is_zoom = False - """ - self.settings.update_instrument() - - -if __name__ == "__main__": - root = tk.Tk() - experimentsettings = ExperimentSettings() - estimation = Estimation() - vm = InstrumentVM(experimentsettings, estimation) - - vm.is_instrument.set("loq") - root.mainloop() \ No newline at end of file diff --git a/ViewModels/SettingsPanelVM.cs b/ViewModels/SettingsPanelVM.cs deleted file mode 100644 index d3b6b2d..0000000 --- a/ViewModels/SettingsPanelVM.cs +++ /dev/null @@ -1,87 +0,0 @@ -using System.Collections.ObjectModel; -using System.ComponentModel; -using System; - -namespace LOQ_Script_Gui -{ - class SettingsPanelVM : INotifyPropertyChanged - { - public event PropertyChangedEventHandler PropertyChanged; - - protected void OnPropertyChanged(string name) - { - PropertyChangedEventHandler handler = PropertyChanged; - if (handler != null) - { - handler(this, new PropertyChangedEventArgs(name)); - } - } - - // Provides the combo box options - public static ObservableCollection OrderChoices - { - get - { - return RunOrder.Choices; - } - } - - // Provides the combo box options - public static ObservableCollection GeometryChoices - { - get - { - return Geometry.Choices; - } - } - - // Provides the combo box options - public static ObservableCollection CollectionModeChoices - { - get - { - return DataCollection.Choices; - } - } - - ExperimentSettings experiment = new ExperimentSettings(); - public ExperimentSettings Experiment - { - get - { - return experiment; - } - set - { - experiment = value; - OnPropertyChanged("Experiment"); - } - } - - InstrumentInfo instInfo; - public InstrumentInfo InstInfo - { - get - { - return instInfo; - } - set - { - instInfo = value; - OnPropertyChanged("InstInfo"); - } - } - - public SettingsPanelVM() - { - // Need a default constructor to keep the databinding happy! - // The other constructor should be called ASAP - instInfo = new InstrumentInfo(); - } - - public SettingsPanelVM(InstrumentInfo instInfo) - { - this.instInfo = instInfo; - } - } -} From 55cf3e3b426dde9a3436be406f0ef7d76d0e3f81 Mon Sep 17 00:00:00 2001 From: Dirk Honecker Date: Tue, 31 Dec 2024 15:12:35 +0000 Subject: [PATCH 61/88] Connect and read settings --- EstimationPanel.py | 26 ++++++++++++----------- MainWindow.py | 40 +++++++++++++++++++----------------- Models/ExperimentSettings.py | 21 ++++++++++--------- Models/InstrumentInfo.py | 12 +++++------ Properties/AssemblyInfo.py | 2 +- Properties/Settings.ini | 6 +++--- Scripting/ScriptGenerator.py | 17 +++++++-------- SettingsPanel.py | 11 +++++----- 8 files changed, 70 insertions(+), 65 deletions(-) diff --git a/EstimationPanel.py b/EstimationPanel.py index e74f8e8..545a462 100644 --- a/EstimationPanel.py +++ b/EstimationPanel.py @@ -5,14 +5,16 @@ from Properties.Settings import Settings class EstimationPanel(ttk.Frame): - def __init__(self, parent, estimate = None): + def __init__(self, parent, estimation = None, settings = None): super().__init__(parent) self.on_calculate_clicked = None - self.estimate = estimate if estimate is not None else Estimation() - self.create_widgets() + self.estimation = estimation if estimation is not None else Estimation() + self.settings = settings if settings is not None else Settings() + + self.create_widgets() def create_widgets(self): - settings = Settings() + #self.pack(fill=tk.BOTH, expand=True) self.grid(sticky="nsew") @@ -27,8 +29,8 @@ def create_widgets(self): ttk.Label(grid_frame, text="Est. count rate:").grid(row=0, column=0, sticky=tk.E) count_rate_frame = ttk.Frame(grid_frame) count_rate_frame.grid(row=0, column=1, columnspan=2) - self.txt_count_rate = ttk.Entry(count_rate_frame, width=10, textvariable=self.estimate.count_rate) - self.txt_count_rate.insert(0, settings.count_rate) # Default value + self.txt_count_rate = ttk.Entry(count_rate_frame, width=10, textvariable=self.estimation.count_rate) + self.txt_count_rate.insert(0, self.settings.count_rate) # Default value self.txt_count_rate.grid(row=0, column=0, padx=5, pady=5) ttk.Label(count_rate_frame, text="µA/hr").grid(row=0, column=1) @@ -36,8 +38,8 @@ def create_widgets(self): ttk.Label(grid_frame, text="Est. time between moves:").grid(row=1, column=0, sticky=tk.E) move_time_frame = ttk.Frame(grid_frame) move_time_frame.grid(row=1, column=1, columnspan=2) - self.txt_move_time = ttk.Entry(move_time_frame, width=10, textvariable=self.estimate.move_time) - self.txt_move_time.insert(0, settings.move_time) # Default value + self.txt_move_time = ttk.Entry(move_time_frame, width=10, textvariable=self.estimation.move_time) + self.txt_move_time.insert(0, self.settings.move_time) # Default value self.txt_move_time.grid(row=0, column=0, padx=5, pady=5) ttk.Label(move_time_frame, text="seconds").grid(row=0, column=1) @@ -45,8 +47,8 @@ def create_widgets(self): ttk.Label(grid_frame, text="Est. script time:").grid(row=2, column=0, sticky=tk.E) script_time_frame = ttk.Frame(grid_frame) script_time_frame.grid(row=2, column=1, columnspan=2) - self.txt_estimate = ttk.Entry(script_time_frame, width=15, textvariable=self.estimate.script_time, state="readonly") - self.txt_estimate.grid(row=0, column=0, padx=5, pady=5) + self.txt_estimation = ttk.Entry(script_time_frame, width=15, textvariable=self.estimation.script_time, state="readonly") + self.txt_estimation.grid(row=0, column=0, padx=5, pady=5) # Calculate button self.btn_calculate = ttk.Button(grid_frame, text="Calculate", command=self.btn_calculate_click) @@ -60,8 +62,8 @@ def btn_calculate_click(self): if __name__ == "__main__": root = tk.Tk() - estimate = EstimationPanelVM() - panel = EstimationPanel(root, estimate) + estimation = EstimationPanelVM() + panel = EstimationPanel(root, estimation) panel.grid(sticky="nsew") root.grid_rowconfigure(0, weight=1) root.grid_columnconfigure(0, weight=1) diff --git a/MainWindow.py b/MainWindow.py index 25d20ae..dbcf52b 100644 --- a/MainWindow.py +++ b/MainWindow.py @@ -28,11 +28,15 @@ def __init__(self,parent): self.parent.geometry("800x600") self.data = DataGridVM() - self.inst_info = InstrumentInfo() - self.settings = SettingsPanel(self, inst_info=self.inst_info) - self.settings.experiment = ExperimentSettings() - self.estimate = Estimation() + self.settings = Settings() + self.estimation = Estimation() + self.settings.experiment = ExperimentSettings(settings=self.settings, estimation=self.estimation) + self.inst_info = InstrumentInfo(settings=self.settings) + + #self.settings.panel = SettingsPanel(self, inst_info=self.inst_info,settings=self.settings) + + #self.instrument = InstrumentVM(self.settings.experiment, self.estimate) self.create_widgets() @@ -58,14 +62,14 @@ def create_widgets(self): """ # Settings Panel - self.pnl_settings = SettingsPanel(self, self.settings) + self.pnl_settings = SettingsPanel(self, inst_info=self.inst_info,settings=self.settings) self.pnl_settings.grid(row=0, column=0, sticky="ew") self.pnl_settings.on_preview_clicked = self.pnl_settings_on_preview_clicked self.pnl_settings.on_write_clicked = self.pnl_settings_on_write_clicked # Estimation Panel - self.pnl_estimate = EstimationPanel(self, self.estimate) - self.pnl_estimate.grid(row=1, column=0, sticky="ew") - self.pnl_estimate.on_calculate_clicked = self.pnl_estimate_on_calculate_clicked + self.pnl_estimation = EstimationPanel(self, estimation=self.estimation,settings=self.settings) + self.pnl_estimation.grid(row=1, column=0, sticky="ew") + self.pnl_estimation.on_calculate_clicked = self.pnl_estimate_on_calculate_clicked # Buttons button_frame = ttk.Frame(self) button_frame.grid(row=2, column=0, padx=5, pady=5, sticky="w") @@ -97,9 +101,8 @@ def create_widgets(self): def onFrameConfigure(self, event): self.canvas.configure(scrollregion=self.canvas.bbox("all")) """ - def load_last_instrument(self): - settings = Settings() - inst = settings.last_instrument # Replace with actual settings retrieval + def load_last_instrument(self): + inst = self.settings.last_instrument # Replace with actual settings retrieval self.instrument_var.set(inst) @@ -115,14 +118,14 @@ def pnl_settings_on_write_clicked(self): file.write(script) def pnl_settings_on_preview_clicked(self): - script_generator= ScriptGenerator(self.instrument_var.get()) - script = script_generator.generate_script(self.data.runs, self.settings.experiment, "my_script") - print(script) - preview_dialog = PreviewDialog(self, script) + self.script_generator= ScriptGenerator(self.instrument_var.get()) + self.script = self.script_generator.generate_script(self.data.runs, self.settings.experiment, "my_script") + print(self.script) + preview_dialog = PreviewDialog(self, self.script) preview_dialog.show() def pnl_estimate_on_calculate_clicked(self): - self.estimate.script_time = self.inst_info.script_generator.estimate_run_time(self.data.runs, self.settings.experiment, self.estimate) + self.estimation.script_time = self.script_generator.estimate_run_time(self.data.runs, self.settings.experiment, self.estimation) def btn_create_new_table_click(self): self.data.runs.clear() @@ -155,9 +158,8 @@ def mnu_save_csv_click(self): messagebox.showerror("SANS Script - Error", "Could not save as CSV") def on_close(self): - # Save last_instrument to settings - settings = Settings() - settings.save() + # Save last_instrument to settings + self.settings.save() self.parent.destroy() diff --git a/Models/ExperimentSettings.py b/Models/ExperimentSettings.py index 4c65eeb..6aa4e8f 100644 --- a/Models/ExperimentSettings.py +++ b/Models/ExperimentSettings.py @@ -28,7 +28,7 @@ def __init__(self, settings=None, estimation=None): self.property_changed = [] self.is_instrument = tk.StringVar(value=self.settings.last_instrument) - + self.is_instrument.trace_add("write", self.update_instrument) self.is_instrument.trace_add("write", self.on_property_changed) self._order.trace_add("write", self.on_property_changed) @@ -52,6 +52,8 @@ def __init__(self, settings=None, estimation=None): self.sans2d_data_collection=DataCollection.get_SANS2D_choices() self.zoom_data_collection=DataCollection.get_ZOOM_choices() + self.update_instrument() + def __call__(self, order=None, num_sans=None, num_trans=None, loop_per_run=None, a2_setting_sans=None, a2_setting_trans=None, sample_geometry=None, sample_height=None, sample_width=None, collection_mode=None, is_valid=None): if order is not None: self.order = order @@ -92,17 +94,16 @@ def update_instrument(self, *args): self.set_instrument(self.is_instrument.get(), "A6 Setting:", self.zoom_aperture_sizes,"Data Acquisition:",self.zoom_data_collection, 40) def set_instrument(self, instrument, a2_label, a2_choices, data_collection_label, data_collection_choices, count_rate): - if not hasattr(self, 'settings') or self.settings is None: - self.settings = Settings() + - self.settings.is_instrument.set(instrument) + self.is_instrument.set(instrument) - self.settings.a2_label = a2_label - self.settings.a2_choices = a2_choices - self.settings.data_collection_label = data_collection_label - self.settings.data_collection_choices = data_collection_choices - self.settings.a2_trans_enabled = True - self.settings.collection_mode_enabled = True + self.a2_label = a2_label + self.a2_choices = a2_choices + self.data_collection_label = data_collection_label + self.data_collection_choices = data_collection_choices + self.a2_trans_enabled = True + self.collection_mode_enabled = True self.estimation.count_rate = count_rate diff --git a/Models/InstrumentInfo.py b/Models/InstrumentInfo.py index 1521f2a..896ba05 100644 --- a/Models/InstrumentInfo.py +++ b/Models/InstrumentInfo.py @@ -16,12 +16,12 @@ class InstrumentInfo: def __init__(self,settings=None): self.settings = settings if settings is not None else Settings() - self._a2_label = StringVar(value="A2 Setting:") - self._a2_choices = tk.Variable(value=["Large", "Medium", "Small"]) - self._data_collection_label = StringVar(value="Data acquisition:") - self._data_collection_choices = tk.Variable(value=["Event", "SESANS"]) - self._a2_trans_enabled = BooleanVar(value=True) - self._collection_mode_enabled = BooleanVar(value=False) + self._a2_label = StringVar(value=self.settings.experiment.a2_label) + self._a2_choices = tk.Variable(value=self.settings.experiment.a2_choices) + self._data_collection_label = StringVar(value=self.settings.experiment.data_collection_label) + self._data_collection_choices = tk.Variable(value=self.settings.experiment.data_collection_choices) + self._a2_trans_enabled = BooleanVar(value=self.settings.experiment.a2_trans_enabled) + self._collection_mode_enabled = BooleanVar(value=self.settings.experiment.collection_mode_enabled) self._script_generator = None diff --git a/Properties/AssemblyInfo.py b/Properties/AssemblyInfo.py index 223c844..e4c5d67 100644 --- a/Properties/AssemblyInfo.py +++ b/Properties/AssemblyInfo.py @@ -17,7 +17,7 @@ "License :: OSI Approved :: MIT License", "Operating System :: OS Independent", ], - python_requires='>=3.6', + python_requires=">=3.6", ) # Additional metadata for localization and resource management can be added as needed. \ No newline at end of file diff --git a/Properties/Settings.ini b/Properties/Settings.ini index b5b631d..51421f9 100644 --- a/Properties/Settings.ini +++ b/Properties/Settings.ini @@ -7,9 +7,9 @@ runorder = All Trans First numtrans = 1 numsans = 1 loopperrun = False -a2settingsans = -a2settingtrans = -collectionmode = +a2settingsans = Large +a2settingtrans = Large +collectionmode = Events tableentries = "POSITION, TRANS, TRANS_WAIT, SANS, SANS_WAIT, PERIOD, SAMPLE_IDENTIFICATION_STRING, BACKGROUND, THICKNESS, TEMPERATURE, TEMPERATURE2, FIELD, SHEAR_RATE_1, SHEAR_RATE_2, SHEAR_ANGLE_1, SHEAR_ANGLE_2, PRE_COMMAND, POST_COMMAND, RB_NUMBER" countrate = 38 movetime = 120 diff --git a/Scripting/ScriptGenerator.py b/Scripting/ScriptGenerator.py index 16e05fc..963df75 100644 --- a/Scripting/ScriptGenerator.py +++ b/Scripting/ScriptGenerator.py @@ -14,6 +14,7 @@ def __init__(self, instrument=None): self.indent = " " self.is_instrument = tk.StringVar() # Initialize the attribute self.is_instrument.set(instrument) + a2setting=A2Setting() def generate_script(self, runs, settings, scriptname): script = f"# Script created by SANSScript at {datetime.now()}\n\n" @@ -158,14 +159,14 @@ def do_sans_run(self, script, exp, indent, settings): return script def aperture_string(self, size): - a2setting = A2Setting() - if size in [a2setting.Large, a2setting.Larmor]: + + if size in [A2setting.Large, A2setting.Larmor]: return "large" - elif size in [a2setting.Medium, a2setting.Larmor]: + elif size in [A2setting.Medium, A2setting.Larmor]: return "medium" - elif size in [a2setting.Small, a2setting.Larmor]: + elif size in [A2setting.Small, A2setting.Larmor]: return "small" - elif size in [a2setting.XLarge, a2setting.Larmor]: + elif size in [A2setting.XLarge, A2setting.Larmor]: return "xlarge" return "" @@ -246,11 +247,11 @@ def get_minutes(wait, length, count_rate): runs = [experiment(position="pos1", trans=10, sans=20, trans_wait="Minutes", sans_wait="Minutes", sample="Sample1", thickness=1.0)] experiment_settings =ExperimentSettings() settings = experiment_settings(sample_width="10", sample_height="10", sample_geometry="Flat", order="AllTrans", num_trans=1, num_sans=1, collection_mode="Events", a2_setting_trans="Large", a2_setting_sans="Large") - estimate=Estimation() - estimation = estimate(count_rate=100, move_time=10) + estimation=Estimation() + estimate = estimation(count_rate=100, move_time=10) instrument = "LARMOR" generator = ScriptGenerator(instrument) script = generator.generate_script(runs, settings, "my_script") print(script) - estimated_time = generator.estimate_run_time(runs, settings, estimation) + estimated_time = generator.estimate_run_time(runs, settings, estimate) print(f"Estimated run time: {estimated_time}") \ No newline at end of file diff --git a/SettingsPanel.py b/SettingsPanel.py index b141465..9ef5eb4 100644 --- a/SettingsPanel.py +++ b/SettingsPanel.py @@ -10,18 +10,18 @@ class SettingsPanel(ttk.Frame): #def __init__(self, parent, experiment=None, inst_info=None): - def __init__(self, parent,inst_info=None): + def __init__(self, parent,inst_info=None, settings=None): super().__init__(parent) self.parent = parent - + self.settings = settings if settings is not None else Settings() self.inst_info = inst_info if inst_info is not None else InstrumentInfo() #self.settings_vm = settings_vm if settings_vm is not None else SettingsPanelVM() #self.instrument_vm = instrument_vm if instrument_vm is not None else InstrumentVM() self.on_preview_clicked = None self.on_write_clicked = None self.property_changed = [] - #self.order_choices = RunOrder.get_choices() - #self.geometry_choices = Geometry.get_choices() + self.order_choices = RunOrder.get_choices() + self.geometry_choices = Geometry.get_choices() #self._experiment = experiment if experiment is not None else ExperimentSettings() #self._inst_info = inst_info if inst_info is not None else InstrumentInfo() @@ -34,8 +34,7 @@ def create_widgets(self): #self.write_button = ttk.Button(self, text="Write Script", command=self.on_write_clicked) #self.write_button.pack(side=tk.LEFT, padx=5, pady=5) - self.settings = Settings() - self.inst_info = InstrumentInfo() + label = ttk.Label(self, text="Settings", font=("Helvetica", 16, "bold")) label.grid(row=0, column=0, columnspan=2, pady=10) From b30760eaec1f8d69b1288c4b3e7a5aa44065ae6e Mon Sep 17 00:00:00 2001 From: Dirk Honecker Date: Tue, 31 Dec 2024 17:43:40 +0000 Subject: [PATCH 62/88] Connect changes of instrument --- MainWindow.py | 21 ++++++++++-------- Models/ExperimentSettings.py | 43 +++++++++++++++++++++++------------- Properties/Settings.ini | 2 +- Properties/Settings.py | 9 ++++---- Scripting/ScriptGenerator.py | 21 +++++++++--------- 5 files changed, 56 insertions(+), 40 deletions(-) diff --git a/MainWindow.py b/MainWindow.py index dbcf52b..f764fd6 100644 --- a/MainWindow.py +++ b/MainWindow.py @@ -92,33 +92,36 @@ def create_widgets(self): instrument_menu = tk.Menu(self.mnu_main, tearoff=0) self.mnu_main.add_cascade(label="Instrument", menu=instrument_menu) - self.instrument_var = tk.StringVar(value="LARMOR") - instrument_menu.add_radiobutton(label="LARMOR", variable=self.instrument_var, value="LARMOR") - instrument_menu.add_radiobutton(label="LOQ", variable=self.instrument_var, value="LOQ") - instrument_menu.add_radiobutton(label="SANS2D", variable=self.instrument_var, value="SANS2D") - instrument_menu.add_radiobutton(label="ZOOM", variable=self.instrument_var, value="ZOOM") + + instrument_menu.add_radiobutton(label="LARMOR", variable=self.settings.experiment.is_instrument, value="LARMOR") + instrument_menu.add_radiobutton(label="LOQ", variable=self.settings.experiment.is_instrument, value="LOQ") + instrument_menu.add_radiobutton(label="SANS2D", variable=self.settings.experiment.is_instrument, value="SANS2D") + instrument_menu.add_radiobutton(label="ZOOM", variable=self.settings.experiment.is_instrument, value="ZOOM") """ def onFrameConfigure(self, event): self.canvas.configure(scrollregion=self.canvas.bbox("all")) """ + + def load_last_instrument(self): inst = self.settings.last_instrument # Replace with actual settings retrieval - self.instrument_var.set(inst) + self.settings.experiment.is_instrument = inst + def pnl_settings_on_write_clicked(self): - filetypes = [("genie_python Files", "*.py")] if self.instrument_var.get() in ["LARMOR", "LOQ", "ZOOM", "SANS2D"] else [("Open Genie Files", "*.gcl")] + filetypes = [("genie_python Files", "*.py")] if self.settings.is_instrument in ["LARMOR", "LOQ", "ZOOM", "SANS2D"] else [("Open Genie Files", "*.gcl")] filepath = filedialog.asksaveasfilename(defaultextension=filetypes[0][1], filetypes=filetypes) if filepath: scriptname = filepath.split("/")[-1].replace(" ", "_").rsplit(".", 1)[0] - script_generator= ScriptGenerator(self.instrument_var.get()) + script_generator= ScriptGenerator(self.settings.is_instrument) script = script_generator.generate_script(self.data.runs, self.settings.experiment, scriptname) with open(filepath, "w") as file: file.write(script) def pnl_settings_on_preview_clicked(self): - self.script_generator= ScriptGenerator(self.instrument_var.get()) + self.script_generator= ScriptGenerator(self.settings.is_instrument) self.script = self.script_generator.generate_script(self.data.runs, self.settings.experiment, "my_script") print(self.script) preview_dialog = PreviewDialog(self, self.script) diff --git a/Models/ExperimentSettings.py b/Models/ExperimentSettings.py index 6aa4e8f..09454fe 100644 --- a/Models/ExperimentSettings.py +++ b/Models/ExperimentSettings.py @@ -27,10 +27,10 @@ def __init__(self, settings=None, estimation=None): self._is_valid = tk.BooleanVar(value=True) self.property_changed = [] - self.is_instrument = tk.StringVar(value=self.settings.last_instrument) + self._is_instrument = tk.StringVar(value=self.settings.last_instrument) + self._is_instrument.trace_add("write", self.on_property_changed) + self._is_instrument.trace_add("write", self.update_instrument) - self.is_instrument.trace_add("write", self.update_instrument) - self.is_instrument.trace_add("write", self.on_property_changed) self._order.trace_add("write", self.on_property_changed) self._num_sans.trace_add("write", self.on_property_changed) self._num_trans.trace_add("write", self.on_property_changed) @@ -54,7 +54,8 @@ def __init__(self, settings=None, estimation=None): self.update_instrument() - def __call__(self, order=None, num_sans=None, num_trans=None, loop_per_run=None, a2_setting_sans=None, a2_setting_trans=None, sample_geometry=None, sample_height=None, sample_width=None, collection_mode=None, is_valid=None): + ''' + def __call__(self, order=None, num_sans=None, num_trans=None, loop_per_run=None, a2_setting_sans=None, a2_setting_trans=None, sample_geometry=None, sample_height=None, sample_width=None, collection_mode=None, is_instrument=None, is_valid=None): if order is not None: self.order = order if num_sans is not None: @@ -77,26 +78,29 @@ def __call__(self, order=None, num_sans=None, num_trans=None, loop_per_run=None, self.collection_mode = collection_mode if is_valid is not None: self.is_valid = is_valid + if is_instrument is not None: + self.is_instrument = is_instrument + ''' def on_property_changed(self, *args): for callback in self.property_changed: callback(args[0]) - self.save() + self.settings.save() def update_instrument(self, *args): - if self.is_instrument.get() == "LARMOR": - self.set_instrument(self.is_instrument.get(), "A1, S1 Setting:", self.larmor_aperture_sizes,"Data Acquisition:",self.larmor_data_collection, 40) - elif self.is_instrument.get() == "LOQ": - self.set_instrument(self.is_instrument.get(), "A2 Setting:", self.loq_aperture_sizes,"Data Acquisition:",self.loq_data_collection, 170) - elif self.is_instrument.get() == "SANS2D": - self.set_instrument(self.is_instrument.get(), "A6 Setting:", self.sans2d_aperture_sizes,"Data Acquisition:",self.sans2d_data_collection, 40) - elif self.is_instrument.get() == "ZOOM": - self.set_instrument(self.is_instrument.get(), "A6 Setting:", self.zoom_aperture_sizes,"Data Acquisition:",self.zoom_data_collection, 40) + if self.is_instrument == "LARMOR": + self.set_instrument(self.is_instrument, "A1, S1 Setting:", self.larmor_aperture_sizes,"Data Acquisition:",self.larmor_data_collection, 40) + elif self.is_instrument == "LOQ": + self.set_instrument(self.is_instrument, "A2 Setting:", self.loq_aperture_sizes,"Data Acquisition:",self.loq_data_collection, 170) + elif self.is_instrument == "SANS2D": + self.set_instrument(self.is_instrument, "A6 Setting:", self.sans2d_aperture_sizes,"Data Acquisition:",self.sans2d_data_collection, 40) + elif self.is_instrument == "ZOOM": + self.set_instrument(self.is_instrument, "A6 Setting:", self.zoom_aperture_sizes,"Data Acquisition:",self.zoom_data_collection, 40) def set_instrument(self, instrument, a2_label, a2_choices, data_collection_label, data_collection_choices, count_rate): - self.is_instrument.set(instrument) + self.settings.is_instrument = instrument self.a2_label = a2_label self.a2_choices = a2_choices @@ -106,7 +110,7 @@ def set_instrument(self, instrument, a2_label, a2_choices, data_collection_label self.collection_mode_enabled = True self.estimation.count_rate = count_rate - + ''' def save(self): self.settings.run_order = self.order @@ -120,6 +124,7 @@ def save(self): self.settings.sample_width = self.sample_width self.settings.collection_mode = self.collection_mode self.settings.save() + ''' @property def order(self): @@ -213,6 +218,14 @@ def collection_mode(self): @collection_mode.setter def collection_mode(self, value): self._collection_mode.set(value) + + @property + def is_instrument(self): + return self._is_instrument.get() + + @is_instrument.setter + def is_instrument(self, value): + self._is_instrument.set(value) @property def is_valid(self): diff --git a/Properties/Settings.ini b/Properties/Settings.ini index 51421f9..9834325 100644 --- a/Properties/Settings.ini +++ b/Properties/Settings.ini @@ -1,5 +1,5 @@ [DEFAULT] -lastinstrument = LOQ +lastinstrument = LARMOR sampleheight = 10.0 samplewidth = 10.0 geometry = Flat Plate diff --git a/Properties/Settings.py b/Properties/Settings.py index 2b2e2eb..1f80c4d 100644 --- a/Properties/Settings.py +++ b/Properties/Settings.py @@ -132,7 +132,8 @@ def save(self): self.dict_settings.write(configfile) # Example usage -settings = Settings() -print(settings.last_instrument) -settings.last_instrument = "LOQ" -print(settings.last_instrument) \ No newline at end of file +if __name__ == "__main__": + settings = Settings() + print(settings.last_instrument) + settings.last_instrument = "LOQ" + print(settings.last_instrument) \ No newline at end of file diff --git a/Scripting/ScriptGenerator.py b/Scripting/ScriptGenerator.py index 963df75..5a4a102 100644 --- a/Scripting/ScriptGenerator.py +++ b/Scripting/ScriptGenerator.py @@ -12,9 +12,8 @@ class ScriptGenerator: def __init__(self, instrument=None): self.indent = " " - self.is_instrument = tk.StringVar() # Initialize the attribute - self.is_instrument.set(instrument) - a2setting=A2Setting() + self.instrument = instrument + self.a2setting=A2Setting() def generate_script(self, runs, settings, scriptname): script = f"# Script created by SANSScript at {datetime.now()}\n\n" @@ -32,13 +31,13 @@ def generate_header(self, scriptname): script += "from genie_python import genie as g, BLOCK_NAMES as b\n" script += "sys.path.append(r\"c:\\instrument\\scripts\")\n" - if self.is_instrument.get() == "LARMOR": + if self.instrument == "LARMOR": script += "from instrument.larmor import * # pylint: disable=wildcard-import, unused-wildcard-import\n" - if self.is_instrument.get() == "LOQ": + if self.instrument == "LOQ": script += "from instrument.loq import * # pylint: disable=wildcard-import, unused-wildcard-import\n" - if self.is_instrument.get() == "ZOOM": + if self.instrument == "ZOOM": script += "from instrument.zoom import * # pylint: disable=wildcard-import, unused-wildcard-import\n" - if self.is_instrument.get() == "SANS2D": + if self.instrument == "SANS2D": script += "from instrument.sans2d import * # pylint: disable=wildcard-import, unused-wildcard-import\n" script += f"\ndef {scriptname}():\n" @@ -53,7 +52,7 @@ def generate_setup(self, settings): def check_positions(self, runs): script = "" - if self.is_instrument.get() == "LARMOR": + if self.instrument == "LARMOR": position_list = ",".join([exp.position for exp in runs if exp.position]) if position_list: script += self.get_indent_string(1) + f"if check_move_pos(['{position_list}']) != True:\n" @@ -123,9 +122,9 @@ def do_trans_run(self, script, exp, indent, settings): waitfor = "seconds" elif exp.trans_wait == "Minutes": waitfor = "minutes" - if self.is_instrument.get() == "LARMOR" or self.is_instrument.get() == "ZOOM": + if self.instrument == "LARMOR" or self.instrument == "ZOOM": script += self.get_indent_string(indent) + f"do_trans(title='{fulltitle}', pos='{exp.position}', {waitfor}={exp.trans}, thickness={exp.thickness})\n" - elif self.is_instrument.get() == "LOQ": + elif self.instrument == "LOQ": script += self.get_indent_string(indent) + f"do_trans(title='{fulltitle}', pos='{exp.position}', {waitfor}={exp.trans}, thickness={exp.thickness}, aperture='small')\n" else: script += self.get_indent_string(indent) + f"do_trans(title='{fulltitle}', pos='{exp.position}', {waitfor}={exp.trans}, thickness={exp.thickness}, dae='transmission', aperture='{self.aperture_string(settings.a2_setting_trans)}')\n" @@ -151,7 +150,7 @@ def do_sans_run(self, script, exp, indent, settings): waitfor = "seconds" elif exp.sans_wait == "Minutes": waitfor = "minutes" - if self.is_instrument.get() == "LARMOR" or self.is_instrument.get() == "ZOOM": + if self.instrument == "LARMOR" or self.instrument == "ZOOM": script += self.get_indent_string(indent) + f"do_sans(title='{fulltitle}', pos='{exp.position}', {waitfor}={exp.sans}, thickness={exp.thickness}, dae='{rtype}')\n" else: script += self.get_indent_string(indent) + f"do_sans(title='{fulltitle}', pos='{exp.position}', {waitfor}={exp.sans}, thickness={exp.thickness}, dae='{rtype}', aperture='{self.aperture_string(settings.a2_setting_sans)}')\n" From ace5c427b183f1504c6be75691fe1f2c846ab793 Mon Sep 17 00:00:00 2001 From: Dirk Honecker Date: Thu, 2 Jan 2025 03:24:56 +0000 Subject: [PATCH 63/88] Update SettingsPanel when instrument changes --- MainWindow.py | 15 +++++++++++---- Models/ExperimentSettings.py | 10 +++++----- 2 files changed, 16 insertions(+), 9 deletions(-) diff --git a/MainWindow.py b/MainWindow.py index f764fd6..428e802 100644 --- a/MainWindow.py +++ b/MainWindow.py @@ -93,15 +93,22 @@ def create_widgets(self): instrument_menu = tk.Menu(self.mnu_main, tearoff=0) self.mnu_main.add_cascade(label="Instrument", menu=instrument_menu) - instrument_menu.add_radiobutton(label="LARMOR", variable=self.settings.experiment.is_instrument, value="LARMOR") - instrument_menu.add_radiobutton(label="LOQ", variable=self.settings.experiment.is_instrument, value="LOQ") - instrument_menu.add_radiobutton(label="SANS2D", variable=self.settings.experiment.is_instrument, value="SANS2D") - instrument_menu.add_radiobutton(label="ZOOM", variable=self.settings.experiment.is_instrument, value="ZOOM") + instrument_menu.add_radiobutton(label="LARMOR", variable=self.settings.experiment.instrument, value="LARMOR", command=self.update_settings_panel) + instrument_menu.add_radiobutton(label="LOQ", variable=self.settings.experiment.instrument, value="LOQ", command=self.update_settings_panel) + instrument_menu.add_radiobutton(label="SANS2D", variable=self.settings.experiment.instrument, value="SANS2D", command=self.update_settings_panel) + instrument_menu.add_radiobutton(label="ZOOM", variable=self.settings.experiment.instrument, value="ZOOM", command=self.update_settings_panel) """ def onFrameConfigure(self, event): self.canvas.configure(scrollregion=self.canvas.bbox("all")) """ + def update_settings_panel(self): + self.pnl_settings.destroy() + self.inst_info = InstrumentInfo(settings=self.settings) + self.pnl_settings = SettingsPanel(self, inst_info=self.inst_info, settings=self.settings) + self.pnl_settings.grid(row=0, column=0, sticky="ew") + self.pnl_settings.on_preview_clicked = self.pnl_settings_on_preview_clicked + self.pnl_settings.on_write_clicked = self.pnl_settings_on_write_clicked def load_last_instrument(self): inst = self.settings.last_instrument # Replace with actual settings retrieval diff --git a/Models/ExperimentSettings.py b/Models/ExperimentSettings.py index 09454fe..4b1de4e 100644 --- a/Models/ExperimentSettings.py +++ b/Models/ExperimentSettings.py @@ -27,9 +27,9 @@ def __init__(self, settings=None, estimation=None): self._is_valid = tk.BooleanVar(value=True) self.property_changed = [] - self._is_instrument = tk.StringVar(value=self.settings.last_instrument) - self._is_instrument.trace_add("write", self.on_property_changed) - self._is_instrument.trace_add("write", self.update_instrument) + self.instrument = tk.StringVar(value=self.settings.last_instrument) + self.instrument.trace_add("write", self.on_property_changed) + self.instrument.trace_add("write", self.update_instrument) self._order.trace_add("write", self.on_property_changed) self._num_sans.trace_add("write", self.on_property_changed) @@ -221,11 +221,11 @@ def collection_mode(self, value): @property def is_instrument(self): - return self._is_instrument.get() + return self.instrument.get() @is_instrument.setter def is_instrument(self, value): - self._is_instrument.set(value) + self.instrument.set(value) @property def is_valid(self): From ffd0d97d5341453a6321d63d5f4f8e486ba59ada Mon Sep 17 00:00:00 2001 From: Dirk Honecker Date: Thu, 13 Aug 2026 14:25:37 +0100 Subject: [PATCH 64/88] resolve merge conflict --- .gitignore | 5 +- MainWindow.py | 27 ++-- Models/Experiment.py | 5 +- Models/RunOrder.py | 8 +- Properties/Settings.ini | 2 +- Scripting/ScriptGenerator.py | 18 +-- TableDataConstructor.py | 96 ++++++++++--- TableTest.py | 18 +-- TextFile2.py | 17 ++- Transcript_GitCopilot.txt | 266 +++++++++++++++++------------------ 10 files changed, 270 insertions(+), 192 deletions(-) diff --git a/.gitignore b/.gitignore index 6488548..1a9e069 100644 --- a/.gitignore +++ b/.gitignore @@ -50,7 +50,7 @@ bld/ # Visual Studio 2015/2017 cache/options directory .vs/ -# Uncomment if you have tasks that create the project's static files in wwwroot +# Uncomment if you have tasks that create the project"s static files in wwwroot #wwwroot/ @@ -268,6 +268,7 @@ PublishScripts/ !**/[Pp]ackages/build/ # Uncomment if necessary however generally it will be regenerated when needed #!**/[Pp]ackages/repositories.config + # NuGet v3's project.json files produces more ignorable files *.nuget.props @@ -426,7 +427,7 @@ __pycache__/ # Tabs Studio *.tss -# Telerik's JustMock configuration file +# Telerik"s JustMock configuration file *.jmconfig # BizTalk build output diff --git a/MainWindow.py b/MainWindow.py index 428e802..b20dac8 100644 --- a/MainWindow.py +++ b/MainWindow.py @@ -16,6 +16,7 @@ from Properties.Settings import Settings from Scripting.ScriptGenerator import ScriptGenerator from Models.ExperimentSettings import ExperimentSettings +from TableDataConstructor import TableDataConstructor TableEntries = "POSITION, TRANS, TRANS_WAIT, SANS, SANS_WAIT, PERIOD, SAMPLE_IDENTIFICATION_STRING, BACKGROUND, THICKNESS, TEMPERATURE, TEMPERATURE2, FIELD, SHEAR_RATE_1, SHEAR_RATE_2, SHEAR_ANGLE_1, SHEAR_ANGLE_2, PRE_COMMAND, POST_COMMAND, RB_NUMBER" @@ -27,7 +28,7 @@ def __init__(self,parent): self.parent.title("SANS Script GUI") self.parent.geometry("800x600") - self.data = DataGridVM() + #self.data = DataGridVM() self.settings = Settings() self.estimation = Estimation() @@ -120,25 +121,31 @@ def load_last_instrument(self): def pnl_settings_on_write_clicked(self): filetypes = [("genie_python Files", "*.py")] if self.settings.is_instrument in ["LARMOR", "LOQ", "ZOOM", "SANS2D"] else [("Open Genie Files", "*.gcl")] filepath = filedialog.asksaveasfilename(defaultextension=filetypes[0][1], filetypes=filetypes) + constructor = TableDataConstructor(TableEntries) + self.data = constructor.create_experiments(self.data_frame.data.values()) if filepath: scriptname = filepath.split("/")[-1].replace(" ", "_").rsplit(".", 1)[0] script_generator= ScriptGenerator(self.settings.is_instrument) - script = script_generator.generate_script(self.data.runs, self.settings.experiment, scriptname) + script = script_generator.generate_script(self.data, self.settings.experiment, scriptname) with open(filepath, "w") as file: file.write(script) def pnl_settings_on_preview_clicked(self): self.script_generator= ScriptGenerator(self.settings.is_instrument) - self.script = self.script_generator.generate_script(self.data.runs, self.settings.experiment, "my_script") + constructor = TableDataConstructor(TableEntries) + self.data = constructor.create_experiments(self.data_frame.data.values()) + self.script = self.script_generator.generate_script(self.data, self.settings.experiment, "my_script") print(self.script) preview_dialog = PreviewDialog(self, self.script) preview_dialog.show() def pnl_estimate_on_calculate_clicked(self): + constructor = TableDataConstructor(TableEntries) + self.data = constructor.create_experiments(self.data_frame.data.values()) self.estimation.script_time = self.script_generator.estimate_run_time(self.data.runs, self.settings.experiment, self.estimation) def btn_create_new_table_click(self): - self.data.runs.clear() + self.data.clear() def btn_open_excel_click(self): filepath = filedialog.askopenfilename(defaultextension=".xlsx", filetypes=[("Excel Files", "*.xls;*.xlsx")]) @@ -147,7 +154,7 @@ def btn_open_excel_click(self): excel = ExcelIO() excel.open_excel_workbook(filepath) exps = excel.extract_experiments() - self.data.runs = exps + self.data = exps except Exception as err: messagebox.showerror("SANS Script - Error", "Could not open Excel spreadsheet") @@ -156,14 +163,18 @@ def mnu_exit_click(self): def mnu_export_to_excel_click(self): excel = ExcelIO() - filepath = filedialog.asksaveasfilename(defaultextension=".xls", filetypes=[("Exel File", "*.xls")]) - excel.export_data_to_excel(filepath,self.data.runs) + filepath = filedialog.asksaveasfilename(defaultextension=".xls", filetypes=[("Exel File", "*.xls")]) + constructor = TableDataConstructor(TableEntries) + self.data = constructor.create_experiments(self.data_frame.data.values()) + excel.export_data_to_excel(filepath,self.runs) def mnu_save_csv_click(self): filepath = filedialog.asksaveasfilename(defaultextension=".csv", filetypes=[("CSV File", "*.csv")]) + constructor = TableDataConstructor(TableEntries) + self.data = constructor.create_experiments(self.data_frame.data.values()) if filepath: try: - CsvExporter.export(filepath, self.data.runs) + CsvExporter.export(filepath, self.data) except Exception as err: messagebox.showerror("SANS Script - Error", "Could not save as CSV") diff --git a/Models/Experiment.py b/Models/Experiment.py index 1d5dbac..cc33835 100644 --- a/Models/Experiment.py +++ b/Models/Experiment.py @@ -114,7 +114,10 @@ def sample(self, value): @property def thickness(self): - return self._thickness.get() + value = self._thickness.get() + if value == "": + raise ValueError("Thickness cannot be an empty string") + return float(value) @thickness.setter def thickness(self, value): diff --git a/Models/RunOrder.py b/Models/RunOrder.py index ea95720..ca5d07d 100644 --- a/Models/RunOrder.py +++ b/Models/RunOrder.py @@ -1,8 +1,8 @@ class RunOrder: - ALL_TRANS = "All TRANS first" - TRANS_FIRST = "Alternate - TRANS first" - ALL_SANS = "All SANS first" - SANS_FIRST = "Alternate - SANS first" + ALL_TRANS = [1, "All TRANS first"] + TRANS_FIRST = [2, "Alternate - TRANS first"] + ALL_SANS = [3, "All SANS first"] + SANS_FIRST = [4, "Alternate - SANS first"] choices = ( ALL_TRANS, diff --git a/Properties/Settings.ini b/Properties/Settings.ini index 9834325..ba14c99 100644 --- a/Properties/Settings.ini +++ b/Properties/Settings.ini @@ -3,7 +3,7 @@ lastinstrument = LARMOR sampleheight = 10.0 samplewidth = 10.0 geometry = Flat Plate -runorder = All Trans First +runorder = 1 numtrans = 1 numsans = 1 loopperrun = False diff --git a/Scripting/ScriptGenerator.py b/Scripting/ScriptGenerator.py index 5a4a102..c4891c2 100644 --- a/Scripting/ScriptGenerator.py +++ b/Scripting/ScriptGenerator.py @@ -7,13 +7,13 @@ from Models.A2Setting import A2Setting from Models.RunOrder import RunOrder from Models.DataCollection import DataCollection -from Models.A2Setting import A2Setting + class ScriptGenerator: def __init__(self, instrument=None): self.indent = " " self.instrument = instrument - self.a2setting=A2Setting() + def generate_script(self, runs, settings, scriptname): script = f"# Script created by SANSScript at {datetime.now()}\n\n" @@ -65,7 +65,7 @@ def generate_runs(self, runs, settings): if not runs: return script - if settings.order == RunOrder.ALL_TRANS: + if float(settings.order) == RunOrder.ALL_TRANS[0]: script += self.get_indent_string(1) + f"for i in range({settings.num_trans}):\n" for exp in runs: if exp.trans: @@ -74,7 +74,7 @@ def generate_runs(self, runs, settings): for exp in runs: if exp.sans: script = self.do_sans_run(script, exp, 2, settings) - elif settings.order == RunOrder.ALL_SANS: + elif float(settings.order) == RunOrder.ALL_SANS[0]: script += self.get_indent_string(1) + f"for i in range({settings.num_sans}):\n" for exp in runs: if exp.sans: @@ -91,7 +91,7 @@ def generate_runs(self, runs, settings): script += self.get_indent_string(1) + "while True:\n" indent = 2 for exp in runs: - if settings.order == RunOrder.TRANS_FIRST: + if float(settings.order) == RunOrder.TRANS_FIRST[0]: if exp.trans: script += self.get_indent_string(indent) + "if count < num_trans:\n" script = self.do_trans_run(script, exp, indent + 1, settings) @@ -159,13 +159,13 @@ def do_sans_run(self, script, exp, indent, settings): def aperture_string(self, size): - if size in [A2setting.Large, A2setting.Larmor]: + if size in [A2Setting.Large, A2Setting.Larmor]: return "large" - elif size in [A2setting.Medium, A2setting.Larmor]: + elif size in [A2Setting.Medium, A2Setting.Larmor]: return "medium" - elif size in [A2setting.Small, A2setting.Larmor]: + elif size in [A2Setting.Small, A2Setting.Larmor]: return "small" - elif size in [A2setting.XLarge, A2setting.Larmor]: + elif size in [A2Setting.XLarge, A2Setting.Larmor]: return "xlarge" return "" diff --git a/TableDataConstructor.py b/TableDataConstructor.py index 9f04b99..d6bc8b0 100644 --- a/TableDataConstructor.py +++ b/TableDataConstructor.py @@ -1,3 +1,6 @@ +import tkinter as tk +from Models.Experiment import Experiment + class TableDataConstructor: def __init__(self, table_entries): self.table_entries = table_entries.split(", ") @@ -21,26 +24,73 @@ def deconstruct_data(self, data_dict): data_list.append(row) return data_list -# Example usage -TableEntries = "POSITION, TRANS, TRANS_WAIT, SANS, SANS_WAIT, PERIOD, SAMPLE_IDENTIFICATION_STRING, BACKGROUND, THICKNESS, TEMPERATURE, TEMPERATURE2, FIELD, SHEAR_RATE_1, SHEAR_RATE_2, SHEAR_ANGLE_1, SHEAR_ANGLE_2, PRE_COMMAND, POST_COMMAND, RB_NUMBER" -constructor = TableDataConstructor(TableEntries) -empty_data = constructor.construct_empty_data() -print(empty_data) - -# Example usage -TableEntries = "col1, col2, label, col3, col4, label2, col5, col6, label3" -constructor = TableDataConstructor(TableEntries) - -# Construct data from a list of lists -data_list = [ - [99.88, 108.79, "rec1", 99.88, 108.79, "rec1", 99.88, 108.79, "rec1"], - [99.88, 108.79, "rec2", 99.88, 108.79, "rec2", 99.88, 108.79, "rec2"], - [99.88, 108.79, "rec3", 99.88, 108.79, "rec3", 99.88, 108.79, "rec3"]] - # Add more rows as needed - -data = constructor.construct_data(data_list) -print("Data:", data) - -# Deconstruct data back to list of lists -deconstructed_data_list = constructor.deconstruct_data(data) -print("Deconstructed Data List:", deconstructed_data_list) + def create_experiments(self, data_list): + experiments = [] + for row in data_list: + experiment = Experiment( + position=row.get("POSITION", ""), + trans=row.get("TRANS", 0), + trans_wait=row.get("TRANS_WAIT", ""), + sans=row.get("SANS", 0), + sans_wait=row.get("SANS_WAIT", ""), + period=row.get("PERIOD", ""), + background=row.get("BACKGROUND", ""), + sample=row.get("SAMPLE_IDENTIFICATION_STRING", ""), + thickness=row.get("THICKNESS", 0), + temperature1=row.get("TEMPERATURE", ""), + temperature2=row.get("TEMPERATURE2", ""), + field=row.get("FIELD", ""), + shear_rate1=row.get("SHEAR_RATE_1", ""), + shear_rate2=row.get("SHEAR_RATE_2", ""), + shear_angle1=row.get("SHEAR_ANGLE_1", ""), + shear_angle2=row.get("SHEAR_ANGLE_2", ""), + pre_command=row.get("PRE_COMMAND", ""), + post_command=row.get("POST_COMMAND", ""), + rb_number=row.get("RB_NUMBER", "") + ) + experiments.append(experiment) + return experiments + +if __name__ == "__main__": + root = tk.Tk() + # Example usage + TableEntries = "POSITION, TRANS, TRANS_WAIT, SANS, SANS_WAIT, PERIOD, SAMPLE_IDENTIFICATION_STRING, BACKGROUND, THICKNESS, TEMPERATURE, TEMPERATURE2, FIELD, SHEAR_RATE_1, SHEAR_RATE_2, SHEAR_ANGLE_1, SHEAR_ANGLE_2, PRE_COMMAND, POST_COMMAND, RB_NUMBER" + constructor = TableDataConstructor(TableEntries) + empty_data = constructor.construct_empty_data() + print(empty_data) + + # Example usage + TableEntries = "col1, col2, label, col3, col4, label2, col5, col6, label3" + constructor = TableDataConstructor(TableEntries) + + # Construct data from a list of lists + data_list = [ + [99.88, 108.79, "rec1", 99.88, 108.79, "rec1", 99.88, 108.79, "rec1"], + [99.88, 108.79, "rec2", 99.88, 108.79, "rec2", 99.88, 108.79, "rec2"], + [99.88, 108.79, "rec3", 99.88, 108.79, "rec3", 99.88, 108.79, "rec3"]] + # Add more rows as needed + + data = constructor.construct_data(data_list) + print("Data:", data) + + # Deconstruct data back to list of lists + deconstructed_data_list = constructor.deconstruct_data(data) + print("Deconstructed Data List:", deconstructed_data_list) + + # Example usage + TableEntries = "POSITION, TRANS, TRANS_WAIT, SANS, SANS_WAIT, PERIOD, SAMPLE_IDENTIFICATION_STRING, BACKGROUND, THICKNESS, TEMPERATURE, TEMPERATURE2, FIELD, SHEAR_RATE_1, SHEAR_RATE_2, SHEAR_ANGLE_1, SHEAR_ANGLE_2, PRE_COMMAND, POST_COMMAND, RB_NUMBER" + constructor = TableDataConstructor(TableEntries) + + # Construct data from a list of lists + data_list = [ + ["Pos1", 10.5, "Wait1", 20.0, "Wait2", "Period1", "Sample1", "Background1", 1.0, "25", "30", "1.5T", "100", "200", "45", "90", "Pre Command1", "Post Command1", "RB12345"], + ["Pos2", 15.0, "Wait3", 25.0, "Wait4", "Period2", "Sample2", "Background2", 2.0, "35", "40", "2.0T", "150", "250", "50", "95", "Pre Command2", "Post Command2", "RB67890"] + ] + + data = constructor.construct_data(data_list) + print("Data:", data) + + # Create experiments from the constructed data + experiments = constructor.create_experiments(data.values()) + for exp in experiments: + print(exp.position, exp.trans, exp.sans, exp.temperature1, exp.rb_number) diff --git a/TableTest.py b/TableTest.py index 6e6d202..ad7ec38 100644 --- a/TableTest.py +++ b/TableTest.py @@ -1,5 +1,5 @@ -''' +""" import tkinter as tk class SimpleTableInput(tk.Frame): @@ -27,7 +27,7 @@ def __init__(self, parent, rows, columns): self.grid_rowconfigure(rows, weight=1) def get(self): - '''Return a list of lists, containing the data in the table''' + """Return a list of lists, containing the data in the table""" result = [] for row in range(self.rows): current_row = [] @@ -38,10 +38,10 @@ def get(self): return result def _validate(self, P): - '''Perform input validation. + """Perform input validation. Allow only an empty value, or a value that can be converted to a float - ''' + """ if P.strip() == "": return True @@ -67,10 +67,10 @@ def on_submit(self): root = tk.Tk() Example(root).pack(side="top", fill="both", expand=True) root.mainloop() -''' +""" -''' +""" move to next column and row with enter and tab import tkinter as tk @@ -94,8 +94,8 @@ def __init__(self): def handle_enter(self, event): # get the row and column of the entry that got the event entry = event.widget - row = int(entry.grid_info()['row']) - column = int(entry.grid_info()['column']) + row = int(entry.grid_info()["row"]) + column = int(entry.grid_info()["column"]) # compute the new row; either the next row or circle # back around to the first row @@ -107,5 +107,5 @@ def handle_enter(self, event): example = Example() tk.mainloop() -''' +""" diff --git a/TextFile2.py b/TextFile2.py index d64e6a0..100c904 100644 --- a/TextFile2.py +++ b/TextFile2.py @@ -4,8 +4,8 @@ import pandas as pd from Models.DataCollection import DataCollection from ViewModels.DataGridVM import DataGridVM -from ViewModels.SettingsPanelVM import SettingsPanelVM -from ViewModels.InstrumentVM import InstrumentVM +#from ViewModels.SettingsPanelVM import SettingsPanelVM +#from ViewModels.InstrumentVM import InstrumentVM from Models.InstrumentInfo import InstrumentInfo from Models.Estimation import Estimation from Models.ExperimentSettings import ExperimentSettings @@ -24,5 +24,18 @@ from Models.ExperimentSettings import ExperimentSettings from Models.A2Setting import A2Setting +if __name__ == "__main__": + root = tk.Tk() + runs = [Experiment(position="pos1", trans=10, sans=20, trans_wait="Minutes", sans_wait="Minutes", sample="Sample1", thickness=1.0)] + settings = ExperimentSettings() + settings(sample_width=10, sample_height=10, sample_geometry="Flat ", order="AllTrans", num_trans=1, num_sans=1, collection_mode="Events", a2_setting_trans="Large", a2_setting_sans="Large") + + estimation = Estimation(count_rate=100, move_time=10) + instrument = "LARMOR" + generator = ScriptGenerator(instrument) + script = generator.generate_script(runs, settings, "my_script") + print(script) + estimated_time = generator.estimate_run_time(runs, settings, estimation) + print(f"Estimated run time: {estimated_time}") diff --git a/Transcript_GitCopilot.txt b/Transcript_GitCopilot.txt index 17862f6..f0585ac 100644 --- a/Transcript_GitCopilot.txt +++ b/Transcript_GitCopilot.txt @@ -272,11 +272,11 @@ import xml.etree.ElementTree as ET root = ET.Element("Project", ToolsVersion="12.0", DefaultTargets="Build", xmlns="http://schemas.microsoft.com/developer/msbuild/2003") -ET.SubElement(root, "Import", Project="$(MSBuildExtensionsPath)\\$(MSBuildToolsVersion)\\Microsoft.Common.props", Condition="Exists('$(MSBuildExtensionsPath)\\$(MSBuildToolsVersion)\\Microsoft.Common.props')") +ET.SubElement(root, "Import", Project="$(MSBuildExtensionsPath)\\$(MSBuildToolsVersion)\\Microsoft.Common.props", Condition="Exists("$(MSBuildExtensionsPath)\\$(MSBuildToolsVersion)\\Microsoft.Common.props")") property_group = ET.SubElement(root, "PropertyGroup") -ET.SubElement(property_group, "Configuration", Condition=" '$(Configuration)' == '' ").text = "Debug" -ET.SubElement(property_group, "Platform", Condition=" '$(Platform)' == '' ").text = "AnyCPU" +ET.SubElement(property_group, "Configuration", Condition=" "$(Configuration)" == "" ").text = "Debug" +ET.SubElement(property_group, "Platform", Condition=" "$(Platform)" == "" ").text = "AnyCPU" ET.SubElement(property_group, "ProjectGuid").text = "{5DBFF2C3-05A0-4B4B-9080-8224D86A7B40}" ET.SubElement(property_group, "OutputType").text = "WinExe" ET.SubElement(property_group, "AppDesignerFolder").text = "Properties" @@ -303,7 +303,7 @@ ET.SubElement(property_group, "ApplicationVersion").text = "1.0.0.%2a" ET.SubElement(property_group, "UseApplicationTrust").text = "false" ET.SubElement(property_group, "BootstrapperEnabled").text = "true" -debug_property_group = ET.SubElement(root, "PropertyGroup", Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ") +debug_property_group = ET.SubElement(root, "PropertyGroup", Condition=" "$(Configuration)|$(Platform)" == "Debug|AnyCPU" ") ET.SubElement(debug_property_group, "PlatformTarget").text = "x64" ET.SubElement(debug_property_group, "DebugSymbols").text = "true" ET.SubElement(debug_property_group, "DebugType").text = "full" @@ -314,7 +314,7 @@ ET.SubElement(debug_property_group, "ErrorReport").text = "prompt" ET.SubElement(debug_property_group, "WarningLevel").text = "4" ET.SubElement(debug_property_group, "Prefer32Bit").text = "false" -release_property_group = ET.SubElement(root, "PropertyGroup", Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ") +release_property_group = ET.SubElement(root, "PropertyGroup", Condition=" "$(Configuration)|$(Platform)" == "Release|AnyCPU" ") ET.SubElement(release_property_group, "PlatformTarget").text = "x64" ET.SubElement(release_property_group, "DebugType").text = "pdbonly" ET.SubElement(release_property_group, "Optimize").text = "true" @@ -520,9 +520,9 @@ class MainWindow(tk.Tk): filetypes = [("genie_python Files", "*.py")] if self.instrument.is_larmor or self.instrument.is_loq or self.instrument.is_zoom or self.instrument.is_sans2d else [("Open Genie Files", "*.gcl")] filepath = filedialog.asksaveasfilename(defaultextension=filetypes[0][1], filetypes=filetypes) if filepath: - scriptname = filepath.split('/')[-1].replace(" ", "_").rsplit('.', 1)[0] + scriptname = filepath.split("/")[-1].replace(" ", "_").rsplit(".", 1)[0] script = self.inst_info.script_generator.generate_script(self.data.runs, self.settings.experiment, scriptname) - with open(filepath, 'w') as file: + with open(filepath, "w") as file: file.write(script) def pnl_settings_on_preview_clicked(self): @@ -975,7 +975,7 @@ class EstimationPanel(ttk.Frame): ttk.Label(grid_frame, text="Est. script time:").grid(row=2, column=0, sticky=tk.E) script_time_frame = ttk.Frame(grid_frame) script_time_frame.grid(row=2, column=1, columnspan=2) - self.txt_estimate = ttk.Entry(script_time_frame, width=15, textvariable=self.view_model.script_time, state='readonly') + self.txt_estimate = ttk.Entry(script_time_frame, width=15, textvariable=self.view_model.script_time, state="readonly") self.txt_estimate.pack(side=tk.LEFT, padx=5, pady=5) # Calculate button @@ -1084,21 +1084,21 @@ import configparser config = configparser.ConfigParser() -config['DEFAULT'] = { - 'LastInstrument': 'LARMOR', - 'SampleHeight': '11', - 'SampleWidth': '11', - 'Geometry': 'Flat Plate', - 'RunOrder': 'Alternate - TRANS first', - 'NumTrans': '1', - 'NumSans': '1', - 'LoopPerRun': 'False', - 'A2SettingSans': 'Large = 25, 16 mm', - 'A2SettingTrans': 'Large = 25, 16 mm', - 'CollectionMode': 'Events' +config["DEFAULT"] = { + "LastInstrument": "LARMOR", + "SampleHeight": "11", + "SampleWidth": "11", + "Geometry": "Flat Plate", + "RunOrder": "Alternate - TRANS first", + "NumTrans": "1", + "NumSans": "1", + "LoopPerRun": "False", + "A2SettingSans": "Large = 25, 16 mm", + "A2SettingTrans": "Large = 25, 16 mm", + "CollectionMode": "Events" } -with open('app_config.ini', 'w') as configfile: +with open("app_config.ini", "w") as configfile: config.write(configfile) ``` @@ -1557,9 +1557,9 @@ class GeniePythonScriptGenerator: def generate_setup(self, settings): script = "" - script += self.get_indent_string(1) + f"g.change_sample_par('Width', '{settings.sample_width}')\n" - script += self.get_indent_string(1) + f"g.change_sample_par('height', '{settings.sample_height}')\n" - script += self.get_indent_string(1) + f"g.change_sample_par('Geometry', '{settings.sample_geometry}')\n\n" + script += self.get_indent_string(1) + f"g.change_sample_par("Width", "{settings.sample_width}")\n" + script += self.get_indent_string(1) + f"g.change_sample_par("height", "{settings.sample_height}")\n" + script += self.get_indent_string(1) + f"g.change_sample_par("Geometry", "{settings.sample_geometry}")\n\n" return script def check_positions(self, runs): @@ -1567,8 +1567,8 @@ class GeniePythonScriptGenerator: if self.is_larmor: position_list = ",".join([exp.position for exp in runs if exp.position]) if position_list: - script += self.get_indent_string(1) + f"if check_move_pos(['{position_list}']) != True:\n" - script += self.get_indent_string(2) + "print('Invalid positions - script aborted')\n" + script += self.get_indent_string(1) + f"if check_move_pos(["{position_list}"]) != True:\n" + script += self.get_indent_string(2) + "print("Invalid positions - script aborted")\n" script += self.get_indent_string(2) + "return\n\n" return script @@ -1635,11 +1635,11 @@ class GeniePythonScriptGenerator: elif exp.trans_wait == "Minutes": waitfor = "minutes" if self.is_larmor or self.is_zoom: - script += self.get_indent_string(indent) + f"do_trans(title='{fulltitle}', pos='{exp.position}', {waitfor}={exp.trans}, thickness={exp.thickness})\n" + script += self.get_indent_string(indent) + f"do_trans(title="{fulltitle}", pos="{exp.position}", {waitfor}={exp.trans}, thickness={exp.thickness})\n" elif self.is_loq: - script += self.get_indent_string(indent) + f"do_trans(title='{fulltitle}', pos='{exp.position}', {waitfor}={exp.trans}, thickness={exp.thickness}, aperture='small')\n" + script += self.get_indent_string(indent) + f"do_trans(title="{fulltitle}", pos="{exp.position}", {waitfor}={exp.trans}, thickness={exp.thickness}, aperture="small")\n" else: - script += self.get_indent_string(indent) + f"do_trans(title='{fulltitle}', pos='{exp.position}', {waitfor}={exp.trans}, thickness={exp.thickness}, dae='transmission', aperture='{self.aperture_string(settings.a2_setting_trans)}')\n" + script += self.get_indent_string(indent) + f"do_trans(title="{fulltitle}", pos="{exp.position}", {waitfor}={exp.trans}, thickness={exp.thickness}, dae="transmission", aperture="{self.aperture_string(settings.a2_setting_trans)}")\n" script += self.get_indent_string(indent) + exp.post_command + "\n" return script @@ -1663,9 +1663,9 @@ class GeniePythonScriptGenerator: elif exp.sans_wait == "Minutes": waitfor = "minutes" if self.is_larmor or self.is_zoom: - script += self.get_indent_string(indent) + f"do_sans(title='{fulltitle}', pos='{exp.position}', {waitfor}={exp.sans}, thickness={exp.thickness}, dae='{rtype}')\n" + script += self.get_indent_string(indent) + f"do_sans(title="{fulltitle}", pos="{exp.position}", {waitfor}={exp.sans}, thickness={exp.thickness}, dae="{rtype}")\n" else: - script += self.get_indent_string(indent) + f"do_sans(title='{fulltitle}', pos='{exp.position}', {waitfor}={exp.sans}, thickness={exp.thickness}, dae='{rtype}', aperture='{self.aperture_string(settings.a2_setting_sans)}')\n" + script += self.get_indent_string(indent) + f"do_sans(title="{fulltitle}", pos="{exp.position}", {waitfor}={exp.sans}, thickness={exp.thickness}, dae="{rtype}", aperture="{self.aperture_string(settings.a2_setting_sans)}")\n" script += self.get_indent_string(indent) + exp.post_command + "\n" return script @@ -1903,15 +1903,15 @@ class OpenGenieScriptGenerator: script += f"LOCAL transCount = 0 sansCount = 0 numTRANS={settings.num_trans} numSANS={settings.num_sans}\n\n" script += "SETSCRIPTNAME THIS_PROCEDURE()\n\n" - script += f'Sample_par width={settings.sample_width} height={settings.sample_height} geometry="{settings.sample_geometry}"\n\n' + script += f"Sample_par width={settings.sample_width} height={settings.sample_height} geometry="{settings.sample_geometry}"\n\n" if self.is_sans2d: position_list = ",".join([exp.position for exp in runs if exp.position]) if position_list: - script += f'IF NOT CHECK_MOVE_POS("{position_list}")\n' - script += ' PRINTEN "Invalid positions - script aborted"\n' - script += ' RETURN\n' - script += 'ENDIF\n\n' + script += f"IF NOT CHECK_MOVE_POS("{position_list}")\n" + script += " PRINTEN "Invalid positions - script aborted"\n" + script += " RETURN\n" + script += "ENDIF\n\n" script += "IF test\n" script += " RETURN\n" script += "ENDIF\n\n" @@ -1965,7 +1965,7 @@ class OpenGenieScriptGenerator: script += "EXITIF ((transCount >= numTRANS) AND (sansCount >= numSANS))\n\n" script += "ENDLOOP\n\n" - script += 'PRINTN "SCRIPT COMPLETED "\n\n' + script += "PRINTN "SCRIPT COMPLETED "\n\n" return script def do_single_sans(self, exp, settings, do_pre_com, do_post_com): @@ -2107,7 +2107,7 @@ class OpenGenieScriptGenerator: return script def create_move_command(self, is_trans, exp, settings): - script = f'MOVE pos="{exp.position}"' + script = f"MOVE pos="{exp.position}"" fulltitle = f"{{{exp.sample}}}" if exp.background: fulltitle += f"_{{{exp.background}}}" @@ -2120,14 +2120,14 @@ class OpenGenieScriptGenerator: if is_trans: script += self.write_wait_for(exp.trans_wait, exp.trans) if fulltitle: - script += f' title="{fulltitle}_TRANS"' + script += f" title="{fulltitle}_TRANS"" else: script += self.write_wait_for(exp.sans_wait, exp.sans) if fulltitle: if settings.collection_mode in ["TRANSSANS_Events", "TRANSSANS_Histogram"]: - script += f' title="{fulltitle}_TRANS_SANS"' + script += f" title="{fulltitle}_TRANS_SANS"" else: - script += f' title="{fulltitle}_SANS"' + script += f" title="{fulltitle}_SANS"" return script + "\n" def write_wait_for(self, waitfor, length): @@ -2261,7 +2261,7 @@ setup( "License :: OSI Approved :: MIT License", "Operating System :: OS Independent", ], - python_requires='>=3.6', + python_requires=">=3.6", ) # Additional metadata for localization and resource management can be added as needed. @@ -2292,8 +2292,8 @@ class Resources: @property def resource_manager(self): if self.resource_man is None: - locale_dir = os.path.join(os.path.dirname(__file__), 'locale') - self.resource_man = gettext.translation('resources', localedir=locale_dir, languages=[self.resource_culture]) + locale_dir = os.path.join(os.path.dirname(__file__), "locale") + self.resource_man = gettext.translation("resources", localedir=locale_dir, languages=[self.resource_culture]) return self.resource_man @property @@ -2307,7 +2307,7 @@ class Resources: # Example usage resources = Resources() -resources.culture = 'en_US' # Set the desired culture +resources.culture = "en_US" # Set the desired culture _ = resources.resource_manager.gettext # Example of using the localized string @@ -2363,23 +2363,23 @@ To read this `.ini` file in Python, you can use the following code: import configparser config = configparser.ConfigParser() -config.read('resources.ini') +config.read("resources.ini") # Accessing resheader values -resmimetype = config['resheader']['resmimetype'] -version = config['resheader']['version'] -reader = config['resheader']['reader'] -writer = config['resheader']['writer'] +resmimetype = config["resheader"]["resmimetype"] +version = config["resheader"]["version"] +reader = config["resheader"]["reader"] +writer = config["resheader"]["writer"] # Accessing data values -name1 = config['data']['Name1'] -color1 = config['data']['Color1'] -bitmap1 = config['data']['Bitmap1'] -icon1 = config['data']['Icon1'] +name1 = config["data"]["Name1"] +color1 = config["data"]["Color1"] +bitmap1 = config["data"]["Bitmap1"] +icon1 = config["data"]["Icon1"] # Accessing metadata comments -name1_comment = config['metadata']['Name1.comment'] -icon1_comment = config['metadata']['Icon1.comment'] +name1_comment = config["metadata"]["Name1.comment"] +icon1_comment = config["metadata"]["Icon1.comment"] print(f"Name1: {name1} (Comment: {name1_comment})") print(f"Color1: {color1}") @@ -2418,118 +2418,118 @@ To read and write these settings in Python, you can use the following code: import configparser class Settings: - def __init__(self, filename='settings.ini'): + def __init__(self, filename="settings.ini"): self.config = configparser.ConfigParser() self.filename = filename self.config.read(self.filename) @property def last_instrument(self): - return self.config['DEFAULT']['LastInstrument'] + return self.config["DEFAULT"]["LastInstrument"] @last_instrument.setter def last_instrument(self, value): - self.config['DEFAULT']['LastInstrument'] = value + self.config["DEFAULT"]["LastInstrument"] = value self.save() @property def sample_height(self): - return self.config['DEFAULT']['SampleHeight'] + return self.config["DEFAULT"]["SampleHeight"] @sample_height.setter def sample_height(self, value): - self.config['DEFAULT']['SampleHeight'] = value + self.config["DEFAULT"]["SampleHeight"] = value self.save() @property def sample_width(self): - return self.config['DEFAULT']['SampleWidth'] + return self.config["DEFAULT"]["SampleWidth"] @sample_width.setter def sample_width(self, value): - self.config['DEFAULT']['SampleWidth'] = value + self.config["DEFAULT"]["SampleWidth"] = value self.save() @property def geometry(self): - return self.config['DEFAULT']['Geometry'] + return self.config["DEFAULT"]["Geometry"] @geometry.setter def geometry(self, value): - self.config['DEFAULT']['Geometry'] = value + self.config["DEFAULT"]["Geometry"] = value self.save() @property def run_order(self): - return self.config['DEFAULT']['RunOrder'] + return self.config["DEFAULT"]["RunOrder"] @run_order.setter def run_order(self, value): - self.config['DEFAULT']['RunOrder'] = value + self.config["DEFAULT"]["RunOrder"] = value self.save() @property def num_trans(self): - return self.config['DEFAULT']['NumTrans'] + return self.config["DEFAULT"]["NumTrans"] @num_trans.setter def num_trans(self, value): - self.config['DEFAULT']['NumTrans'] = value + self.config["DEFAULT"]["NumTrans"] = value self.save() @property def num_sans(self): - return self.config['DEFAULT']['NumSans'] + return self.config["DEFAULT"]["NumSans"] @num_sans.setter def num_sans(self, value): - self.config['DEFAULT']['NumSans'] = value + self.config["DEFAULT"]["NumSans"] = value self.save() @property def loop_per_run(self): - return self.config['DEFAULT']['LoopPerRun'] + return self.config["DEFAULT"]["LoopPerRun"] @loop_per_run.setter def loop_per_run(self, value): - self.config['DEFAULT']['LoopPerRun'] = value + self.config["DEFAULT"]["LoopPerRun"] = value self.save() @property def a2_setting_sans(self): - return self.config['DEFAULT']['A2SettingSans'] + return self.config["DEFAULT"]["A2SettingSans"] @a2_setting_sans.setter def a2_setting_sans(self, value): - self.config['DEFAULT']['A2SettingSans'] = value + self.config["DEFAULT"]["A2SettingSans"] = value self.save() @property def a2_setting_trans(self): - return self.config['DEFAULT']['A2SettingTrans'] + return self.config["DEFAULT"]["A2SettingTrans"] @a2_setting_trans.setter def a2_setting_trans(self, value): - self.config['DEFAULT']['A2SettingTrans'] = value + self.config["DEFAULT"]["A2SettingTrans"] = value self.save() @property def collection_mode(self): - return self.config['DEFAULT']['CollectionMode'] + return self.config["DEFAULT"]["CollectionMode"] @collection_mode.setter def collection_mode(self, value): - self.config['DEFAULT']['CollectionMode'] = value + self.config["DEFAULT"]["CollectionMode"] = value self.save() def save(self): - with open(self.filename, 'w') as configfile: + with open(self.filename, "w") as configfile: self.config.write(configfile) # Example usage settings = Settings() print(settings.last_instrument) -settings.last_instrument = 'LOQ' +settings.last_instrument = "LOQ" print(settings.last_instrument) ``` @@ -2572,118 +2572,118 @@ To read and write these settings in Python, you can use the following code: import configparser class Settings: - def __init__(self, filename='settings.ini'): + def __init__(self, filename="settings.ini"): self.config = configparser.ConfigParser() self.filename = filename self.config.read(self.filename) @property def last_instrument(self): - return self.config['DEFAULT']['LastInstrument'] + return self.config["DEFAULT"]["LastInstrument"] @last_instrument.setter def last_instrument(self, value): - self.config['DEFAULT']['LastInstrument'] = value + self.config["DEFAULT"]["LastInstrument"] = value self.save() @property def sample_height(self): - return self.config['DEFAULT']['SampleHeight'] + return self.config["DEFAULT"]["SampleHeight"] @sample_height.setter def sample_height(self, value): - self.config['DEFAULT']['SampleHeight'] = value + self.config["DEFAULT"]["SampleHeight"] = value self.save() @property def sample_width(self): - return self.config['DEFAULT']['SampleWidth'] + return self.config["DEFAULT"]["SampleWidth"] @sample_width.setter def sample_width(self, value): - self.config['DEFAULT']['SampleWidth'] = value + self.config["DEFAULT"]["SampleWidth"] = value self.save() @property def geometry(self): - return self.config['DEFAULT']['Geometry'] + return self.config["DEFAULT"]["Geometry"] @geometry.setter def geometry(self, value): - self.config['DEFAULT']['Geometry'] = value + self.config["DEFAULT"]["Geometry"] = value self.save() @property def run_order(self): - return self.config['DEFAULT']['RunOrder'] + return self.config["DEFAULT"]["RunOrder"] @run_order.setter def run_order(self, value): - self.config['DEFAULT']['RunOrder'] = value + self.config["DEFAULT"]["RunOrder"] = value self.save() @property def num_trans(self): - return self.config['DEFAULT']['NumTrans'] + return self.config["DEFAULT"]["NumTrans"] @num_trans.setter def num_trans(self, value): - self.config['DEFAULT']['NumTrans'] = value + self.config["DEFAULT"]["NumTrans"] = value self.save() @property def num_sans(self): - return self.config['DEFAULT']['NumSans'] + return self.config["DEFAULT"]["NumSans"] @num_sans.setter def num_sans(self, value): - self.config['DEFAULT']['NumSans'] = value + self.config["DEFAULT"]["NumSans"] = value self.save() @property def loop_per_run(self): - return self.config['DEFAULT']['LoopPerRun'] + return self.config["DEFAULT"]["LoopPerRun"] @loop_per_run.setter def loop_per_run(self, value): - self.config['DEFAULT']['LoopPerRun'] = value + self.config["DEFAULT"]["LoopPerRun"] = value self.save() @property def a2_setting_sans(self): - return self.config['DEFAULT']['A2SettingSans'] + return self.config["DEFAULT"]["A2SettingSans"] @a2_setting_sans.setter def a2_setting_sans(self, value): - self.config['DEFAULT']['A2SettingSans'] = value + self.config["DEFAULT"]["A2SettingSans"] = value self.save() @property def a2_setting_trans(self): - return self.config['DEFAULT']['A2SettingTrans'] + return self.config["DEFAULT"]["A2SettingTrans"] @a2_setting_trans.setter def a2_setting_trans(self, value): - self.config['DEFAULT']['A2SettingTrans'] = value + self.config["DEFAULT"]["A2SettingTrans"] = value self.save() @property def collection_mode(self): - return self.config['DEFAULT']['CollectionMode'] + return self.config["DEFAULT"]["CollectionMode"] @collection_mode.setter def collection_mode(self, value): - self.config['DEFAULT']['CollectionMode'] = value + self.config["DEFAULT"]["CollectionMode"] = value self.save() def save(self): - with open(self.filename, 'w') as configfile: + with open(self.filename, "w") as configfile: self.config.write(configfile) # Example usage settings = Settings() print(settings.last_instrument) -settings.last_instrument = 'LOQ' +settings.last_instrument = "LOQ" print(settings.last_instrument) ``` @@ -3134,22 +3134,22 @@ import tkinter as tk from configparser import ConfigParser class ExperimentSettings: - def __init__(self, config_file='settings.ini'): + def __init__(self, config_file="settings.ini"): self.config = ConfigParser() self.config.read(config_file) self.config_file = config_file - self._order = tk.StringVar(value=self.config['DEFAULT'].get('RunOrder', 'TransFirst')) - self._num_sans = tk.IntVar(value=self.config['DEFAULT'].getint('NumSans', 1)) - self._num_trans = tk.IntVar(value=self.config['DEFAULT'].getint('NumTrans', 1)) - self._loop_per_run = tk.BooleanVar(value=self.config['DEFAULT'].getboolean('LoopPerRun', False)) - self._a2_setting_sans = tk.StringVar(value=self.config['DEFAULT'].get('A2SettingSans', 'Large')) - self._a2_setting_trans = tk.StringVar(value=self.config['DEFAULT'].get('A2SettingTrans', 'Large')) - self._sample_geometry = tk.StringVar(value=self.config['DEFAULT'].get('Geometry', 'Flat Plate')) - self._sample_height = tk.DoubleVar(value=self.config['DEFAULT'].getfloat('SampleHeight', 11)) - self._sample_width = tk.DoubleVar(value=self.config['DEFAULT'].getfloat('SampleWidth', 11)) + self._order = tk.StringVar(value=self.config["DEFAULT"].get("RunOrder", "TransFirst")) + self._num_sans = tk.IntVar(value=self.config["DEFAULT"].getint("NumSans", 1)) + self._num_trans = tk.IntVar(value=self.config["DEFAULT"].getint("NumTrans", 1)) + self._loop_per_run = tk.BooleanVar(value=self.config["DEFAULT"].getboolean("LoopPerRun", False)) + self._a2_setting_sans = tk.StringVar(value=self.config["DEFAULT"].get("A2SettingSans", "Large")) + self._a2_setting_trans = tk.StringVar(value=self.config["DEFAULT"].get("A2SettingTrans", "Large")) + self._sample_geometry = tk.StringVar(value=self.config["DEFAULT"].get("Geometry", "Flat Plate")) + self._sample_height = tk.DoubleVar(value=self.config["DEFAULT"].getfloat("SampleHeight", 11)) + self._sample_width = tk.DoubleVar(value=self.config["DEFAULT"].getfloat("SampleWidth", 11)) self._sample_width_enabled = tk.BooleanVar(value=True) - self._collection_mode = tk.StringVar(value=self.config['DEFAULT'].get('CollectionMode', 'Events')) + self._collection_mode = tk.StringVar(value=self.config["DEFAULT"].get("CollectionMode", "Events")) self._is_valid = tk.BooleanVar(value=True) self.property_changed = [] @@ -3170,17 +3170,17 @@ class ExperimentSettings: self.save() def save(self): - self.config['DEFAULT']['RunOrder'] = self._order.get() - self.config['DEFAULT']['NumSans'] = str(self._num_sans.get()) - self.config['DEFAULT']['NumTrans'] = str(self._num_trans.get()) - self.config['DEFAULT']['LoopPerRun'] = str(self._loop_per_run.get()) - self.config['DEFAULT']['A2SettingSans'] = self._a2_setting_sans.get() - self.config['DEFAULT']['A2SettingTrans'] = self._a2_setting_trans.get() - self.config['DEFAULT']['Geometry'] = self._sample_geometry.get() - self.config['DEFAULT']['SampleHeight'] = str(self._sample_height.get()) - self.config['DEFAULT']['SampleWidth'] = str(self._sample_width.get()) - self.config['DEFAULT']['CollectionMode'] = self._collection_mode.get() - with open(self.config_file, 'w') as configfile: + self.config["DEFAULT"]["RunOrder"] = self._order.get() + self.config["DEFAULT"]["NumSans"] = str(self._num_sans.get()) + self.config["DEFAULT"]["NumTrans"] = str(self._num_trans.get()) + self.config["DEFAULT"]["LoopPerRun"] = str(self._loop_per_run.get()) + self.config["DEFAULT"]["A2SettingSans"] = self._a2_setting_sans.get() + self.config["DEFAULT"]["A2SettingTrans"] = self._a2_setting_trans.get() + self.config["DEFAULT"]["Geometry"] = self._sample_geometry.get() + self.config["DEFAULT"]["SampleHeight"] = str(self._sample_height.get()) + self.config["DEFAULT"]["SampleWidth"] = str(self._sample_width.get()) + self.config["DEFAULT"]["CollectionMode"] = self._collection_mode.get() + with open(self.config_file, "w") as configfile: self.config.write(configfile) @property @@ -3238,7 +3238,7 @@ class ExperimentSettings: @sample_geometry.setter def sample_geometry(self, value): self._sample_geometry.set(value) - self.sample_width_enabled = value != 'Disc' + self.sample_width_enabled = value != "Disc" @property def sample_height(self): @@ -3588,7 +3588,7 @@ import csv class CsvExporter: @staticmethod def export(file, runs): - with open(file, mode='w', newline='') as csvfile: + with open(file, mode="w", newline="") as csvfile: writer = csv.writer(csvfile) for exp in runs: row = [ @@ -3641,7 +3641,7 @@ if __name__ == "__main__": runs = [ Experiment(position="pos1", trans=10, trans_wait="Minutes", sans=20, sans_wait="Minutes", period="1", sample="Sample1", background="Background1", thickness="1.0", temperature1="25", temperature2="30", field="1.5T", shear_rate1="100", shear_rate2="200", shear_angle1="45", shear_angle2="90", pre_command="Pre Command", post_command="Post Command", rb_number="RB12345") ] - CsvExporter.export('output.csv', runs) + CsvExporter.export("output.csv", runs) ``` In this Python version: @@ -3768,8 +3768,8 @@ if __name__ == "__main__": Experiment(position="pos1", trans=10, trans_wait="Minutes", sans=20, sans_wait="Minutes", period="1", sample="Sample1", background="Background1", thickness="1.0", temperature1="25", temperature2="30", field="1.5T", shear_rate1="100", shear_rate2="200", shear_angle1="45", shear_angle2="90", pre_command="Pre Command", post_command="Post Command", rb_number="RB12345") ] excel_io = ExcelIO() - excel_io.export_data_to_excel(runs, 'output.xlsx') - excel_io.open_excel_workbook('output.xlsx') + excel_io.export_data_to_excel(runs, "output.xlsx") + excel_io.open_excel_workbook("output.xlsx") experiments = excel_io.extract_experiments() for exp in experiments: print(exp.__dict__) From e2efadacb43a3fc1fd61bea35570f12481e2da19 Mon Sep 17 00:00:00 2001 From: Dirk Honecker Date: Sat, 4 Jan 2025 08:28:30 +0000 Subject: [PATCH 65/88] runorder fixed by ordinal number instead of string --- Models/ExperimentSettings.py | 3 ++- Models/RunOrder.py | 25 ++++++++++++++++++++----- SettingsPanel.py | 2 +- 3 files changed, 23 insertions(+), 7 deletions(-) diff --git a/Models/ExperimentSettings.py b/Models/ExperimentSettings.py index 4b1de4e..e0589bc 100644 --- a/Models/ExperimentSettings.py +++ b/Models/ExperimentSettings.py @@ -5,6 +5,7 @@ from Models.InstrumentInfo import InstrumentInfo from Models.Estimation import Estimation from Properties.Settings import Settings +from Models.RunOrder import RunOrder class ExperimentSettings: def __init__(self, settings=None, estimation=None): @@ -12,7 +13,7 @@ def __init__(self, settings=None, estimation=None): self.estimation = estimation if estimation is not None else Estimation() - self._order = tk.StringVar(value=self.settings.run_order) + self._order = tk.StringVar(value=RunOrder.get_choice_string(self.settings.run_order)) self._num_sans = tk.IntVar(value=self.settings.num_sans) self._num_trans = tk.IntVar(value=self.settings.num_trans) self._loop_per_run = tk.BooleanVar(value=self.settings.loop_per_run) diff --git a/Models/RunOrder.py b/Models/RunOrder.py index ca5d07d..400382f 100644 --- a/Models/RunOrder.py +++ b/Models/RunOrder.py @@ -1,10 +1,17 @@ class RunOrder: - ALL_TRANS = [1, "All TRANS first"] - TRANS_FIRST = [2, "Alternate - TRANS first"] - ALL_SANS = [3, "All SANS first"] - SANS_FIRST = [4, "Alternate - SANS first"] + ALL_TRANS = [1, "1: All TRANS first"] + TRANS_FIRST = [2, "2: Alternate - TRANS first"] + ALL_SANS = [3, "3: SANS first"] + SANS_FIRST = [4, "4: Alternate - SANS first"] choices = ( + ALL_TRANS[1], + TRANS_FIRST[1], + ALL_SANS[1], + SANS_FIRST[1] + ) + + list_order = ( ALL_TRANS, TRANS_FIRST, ALL_SANS, @@ -15,6 +22,14 @@ def get_choices(): return RunOrder.choices + @staticmethod + def get_choice_string(ordinal): + for choice in RunOrder.list_order: + if choice[0] == float(ordinal): + return choice[1] + return None + # Example usage if __name__ == "__main__": - print(RunOrder.get_choices()) \ No newline at end of file + print(RunOrder.get_choices()) + print(RunOrder.get_choice_string(1)) \ No newline at end of file diff --git a/SettingsPanel.py b/SettingsPanel.py index 9ef5eb4..2f97c15 100644 --- a/SettingsPanel.py +++ b/SettingsPanel.py @@ -50,7 +50,7 @@ def create_widgets(self): self.com_order = ttk.Combobox(grid_frame, values=self.order_choices) self.com_order["values"]=self.order_choices self.com_order.grid(row=0, column=1, columnspan=2, padx=5, pady=5) - self.com_order.set(self.settings.run_order) # Set default value + self.com_order.set(RunOrder.get_choice_string(self.settings.run_order)) # Set default value ''' # Collection Mode ttk.Label(grid_frame, text="Collection Mode:").grid(row=1, column=0, sticky=tk.E) From 2ecd80a9882765cd889dc0307796b64100309a89 Mon Sep 17 00:00:00 2001 From: Dirk Honecker Date: Sat, 4 Jan 2025 08:40:43 +0000 Subject: [PATCH 66/88] Geometry order defined by ordinal number instead of string --- Models/ExperimentSettings.py | 3 ++- Models/Geometry.py | 28 ++++++++++++++++++++++------ Properties/Settings.ini | 2 +- SettingsPanel.py | 7 +++---- 4 files changed, 28 insertions(+), 12 deletions(-) diff --git a/Models/ExperimentSettings.py b/Models/ExperimentSettings.py index e0589bc..9e2ca1e 100644 --- a/Models/ExperimentSettings.py +++ b/Models/ExperimentSettings.py @@ -6,6 +6,7 @@ from Models.Estimation import Estimation from Properties.Settings import Settings from Models.RunOrder import RunOrder +from Models.Geometry import Geometry class ExperimentSettings: def __init__(self, settings=None, estimation=None): @@ -19,7 +20,7 @@ def __init__(self, settings=None, estimation=None): self._loop_per_run = tk.BooleanVar(value=self.settings.loop_per_run) self._a2_setting_sans = tk.StringVar(value=self.settings.a2_setting_sans) self._a2_setting_trans = tk.StringVar(value=self.settings.a2_setting_trans) - self._sample_geometry = tk.StringVar(value=self.settings.geometry) + self._sample_geometry = tk.StringVar(value=Geometry.get_choice_string(self.settings.geometry)) self._sample_height = tk.DoubleVar(value=self.settings.sample_height) self._sample_width = tk.DoubleVar(value=self.settings.sample_width) self._sample_width_enabled = tk.BooleanVar(value=True) diff --git a/Models/Geometry.py b/Models/Geometry.py index 6e91165..78ca559 100644 --- a/Models/Geometry.py +++ b/Models/Geometry.py @@ -1,20 +1,36 @@ class Geometry: - DISC = "Disc" - CYLINDRICAL = "Cylindrical" - FLAT_PLATE = "Flat Plate" - SINGLE_CRYSTAL = "Single Crystal" + DISC = [1, "1: Disc"] + CYLINDRICAL = [2, "2: Cylindrical"] + FLAT_PLATE = [3, "3: Flat Plate"] + SINGLE_CRYSTAL = [4, "4: Single Crystal"] + choices = [ + DISC[1], + CYLINDRICAL[1], + FLAT_PLATE[1], + SINGLE_CRYSTAL[1] + ] + + list_order = ( DISC, CYLINDRICAL, FLAT_PLATE, SINGLE_CRYSTAL - ] + ) @staticmethod def get_choices(): return Geometry.choices + @staticmethod + def get_choice_string(ordinal): + for choice in Geometry.list_order: + if choice[0] == float(ordinal): + return choice[1] + return None + # Example usage if __name__ == "__main__": - print(Geometry.get_choices()) \ No newline at end of file + print(Geometry.get_choices()) + print(Geometry.get_choice_string(1)) \ No newline at end of file diff --git a/Properties/Settings.ini b/Properties/Settings.ini index ba14c99..3ddacf2 100644 --- a/Properties/Settings.ini +++ b/Properties/Settings.ini @@ -2,7 +2,7 @@ lastinstrument = LARMOR sampleheight = 10.0 samplewidth = 10.0 -geometry = Flat Plate +geometry = 3 runorder = 1 numtrans = 1 numsans = 1 diff --git a/SettingsPanel.py b/SettingsPanel.py index 2f97c15..20974c4 100644 --- a/SettingsPanel.py +++ b/SettingsPanel.py @@ -44,9 +44,7 @@ def create_widgets(self): # Order ttk.Label(grid_frame, text="Order:").grid(row=0, column=0, sticky=tk.E) - print(self.order_choices) - print(self.settings.run_order) - + self.com_order = ttk.Combobox(grid_frame, values=self.order_choices) self.com_order["values"]=self.order_choices self.com_order.grid(row=0, column=1, columnspan=2, padx=5, pady=5) @@ -109,7 +107,8 @@ def create_widgets(self): ttk.Label(grid_frame, text="Sample Geometry:").grid(row=7, column=0, sticky=tk.E) self.com_sample_geo = ttk.Combobox(grid_frame, values=self.geometry_choices) self.com_sample_geo.grid(row=7, column=1, columnspan=2, padx=5, pady=5) - self.com_sample_geo.set(self.settings.geometry) # Set default value + self.com_sample_geo.set(Geometry.get_choice_string(self.settings.geometry)) # Set default value + # Sample Height ttk.Label(grid_frame, text="Sample Height:").grid(row=8, column=0, sticky=tk.E) From 2785d4f0577a132f165169f313f405fc3a914919 Mon Sep 17 00:00:00 2001 From: Dirk Honecker Date: Sun, 5 Jan 2025 11:18:37 +0000 Subject: [PATCH 67/88] Update and saving parameters, GUI update and resetting when changing instrument --- EstimationPanel.py | 24 ++++++++--- MainWindow.py | 39 +++++++++-------- Models/A2Setting.py | 6 ++- Models/Estimation.py | 35 +++++++++++---- Models/ExperimentSettings.py | 54 +++++++++++++++++------ Models/Geometry.py | 7 +++ Models/RunOrder.py | 10 ++++- Properties/Settings.ini | 22 +++++----- Scripting/ScriptGenerator.py | 8 ++-- SettingsPanel.py | 65 ++++++++++++++++++++-------- ViewModels/DataGridVM.py | 47 -------------------- ViewModels/EstimationPanelVM.py | 28 ------------ ViewModels/SettingsPanelVM.py | 76 --------------------------------- ViewModels/WaitForUnits.py | 4 -- 14 files changed, 193 insertions(+), 232 deletions(-) delete mode 100644 ViewModels/DataGridVM.py delete mode 100644 ViewModels/EstimationPanelVM.py delete mode 100644 ViewModels/SettingsPanelVM.py delete mode 100644 ViewModels/WaitForUnits.py diff --git a/EstimationPanel.py b/EstimationPanel.py index 545a462..8898781 100644 --- a/EstimationPanel.py +++ b/EstimationPanel.py @@ -1,6 +1,5 @@ import tkinter as tk from tkinter import ttk -from ViewModels.EstimationPanelVM import EstimationPanelVM from Models.Estimation import Estimation from Properties.Settings import Settings @@ -8,8 +7,8 @@ class EstimationPanel(ttk.Frame): def __init__(self, parent, estimation = None, settings = None): super().__init__(parent) self.on_calculate_clicked = None - self.estimation = estimation if estimation is not None else Estimation() self.settings = settings if settings is not None else Settings() + self.estimation = estimation if estimation is not None else Estimation(settings=self.settings) self.create_widgets() @@ -29,19 +28,23 @@ def create_widgets(self): ttk.Label(grid_frame, text="Est. count rate:").grid(row=0, column=0, sticky=tk.E) count_rate_frame = ttk.Frame(grid_frame) count_rate_frame.grid(row=0, column=1, columnspan=2) - self.txt_count_rate = ttk.Entry(count_rate_frame, width=10, textvariable=self.estimation.count_rate) + self.txt_count_rate = ttk.Entry(count_rate_frame, width=10) self.txt_count_rate.insert(0, self.settings.count_rate) # Default value self.txt_count_rate.grid(row=0, column=0, padx=5, pady=5) ttk.Label(count_rate_frame, text="µA/hr").grid(row=0, column=1) + self.txt_count_rate.bind("", self.update_count_rate) # Est. time between moves ttk.Label(grid_frame, text="Est. time between moves:").grid(row=1, column=0, sticky=tk.E) move_time_frame = ttk.Frame(grid_frame) move_time_frame.grid(row=1, column=1, columnspan=2) - self.txt_move_time = ttk.Entry(move_time_frame, width=10, textvariable=self.estimation.move_time) + self.txt_move_time = ttk.Entry(move_time_frame, width=10) self.txt_move_time.insert(0, self.settings.move_time) # Default value self.txt_move_time.grid(row=0, column=0, padx=5, pady=5) ttk.Label(move_time_frame, text="seconds").grid(row=0, column=1) + self.txt_move_time.bind("", self.update_move_time) + + # Est. script time ttk.Label(grid_frame, text="Est. script time:").grid(row=2, column=0, sticky=tk.E) @@ -54,6 +57,17 @@ def create_widgets(self): self.btn_calculate = ttk.Button(grid_frame, text="Calculate", command=self.btn_calculate_click) self.btn_calculate.grid(row=3, column=1, columnspan=2, pady=10) + + # Define the Settings update methods + def update_count_rate(self, event): + self.settings.count_rate = self.txt_count_rate.get() + + def update_move_time(self, event): + self.settings.move_time = self.txt_move_time.get() + + + + def btn_calculate_click(self): if self.on_calculate_clicked: self.on_calculate_clicked() @@ -62,7 +76,7 @@ def btn_calculate_click(self): if __name__ == "__main__": root = tk.Tk() - estimation = EstimationPanelVM() + estimation = Estimation() panel = EstimationPanel(root, estimation) panel.grid(sticky="nsew") root.grid_rowconfigure(0, weight=1) diff --git a/MainWindow.py b/MainWindow.py index b20dac8..6754adf 100644 --- a/MainWindow.py +++ b/MainWindow.py @@ -2,9 +2,6 @@ from tkinter import ttk, filedialog, messagebox from tkinter.scrolledtext import ScrolledText from Models.DataCollection import DataCollection -from ViewModels.DataGridVM import DataGridVM -#from ViewModels.SettingsPanelVM import SettingsPanelVM -#from ViewModels.InstrumentVM import InstrumentVM from Models.InstrumentInfo import InstrumentInfo from Models.Estimation import Estimation from IO.ExcelIO import ExcelIO @@ -28,18 +25,12 @@ def __init__(self,parent): self.parent.title("SANS Script GUI") self.parent.geometry("800x600") - #self.data = DataGridVM() self.settings = Settings() - self.estimation = Estimation() + self.estimation = Estimation(settings=self.settings) self.settings.experiment = ExperimentSettings(settings=self.settings, estimation=self.estimation) self.inst_info = InstrumentInfo(settings=self.settings) - #self.settings.panel = SettingsPanel(self, inst_info=self.inst_info,settings=self.settings) - - - #self.instrument = InstrumentVM(self.settings.experiment, self.estimate) - self.create_widgets() self.load_last_instrument() @@ -86,7 +77,7 @@ def create_widgets(self): file_menu = tk.Menu(self.mnu_main, tearoff=0) self.mnu_main.add_cascade(label="File", menu=file_menu) - file_menu.add_command(label="Export to Excel", command=self.mnu_export_to_excel_click) + #file_menu.add_command(label="Export to Excel", command=self.mnu_export_to_excel_click) file_menu.add_command(label="Save as CSV", command=self.mnu_save_csv_click) file_menu.add_separator() file_menu.add_command(label="Exit", command=self.mnu_exit_click) @@ -94,10 +85,10 @@ def create_widgets(self): instrument_menu = tk.Menu(self.mnu_main, tearoff=0) self.mnu_main.add_cascade(label="Instrument", menu=instrument_menu) - instrument_menu.add_radiobutton(label="LARMOR", variable=self.settings.experiment.instrument, value="LARMOR", command=self.update_settings_panel) - instrument_menu.add_radiobutton(label="LOQ", variable=self.settings.experiment.instrument, value="LOQ", command=self.update_settings_panel) - instrument_menu.add_radiobutton(label="SANS2D", variable=self.settings.experiment.instrument, value="SANS2D", command=self.update_settings_panel) - instrument_menu.add_radiobutton(label="ZOOM", variable=self.settings.experiment.instrument, value="ZOOM", command=self.update_settings_panel) + instrument_menu.add_radiobutton(label="LARMOR", variable=self.settings.experiment.instrument, value="LARMOR", command=self.update_panel) + instrument_menu.add_radiobutton(label="LOQ", variable=self.settings.experiment.instrument, value="LOQ", command=self.update_panel) + instrument_menu.add_radiobutton(label="SANS2D", variable=self.settings.experiment.instrument, value="SANS2D", command=self.update_panel) + instrument_menu.add_radiobutton(label="ZOOM", variable=self.settings.experiment.instrument, value="ZOOM", command=self.update_panel) """ def onFrameConfigure(self, event): self.canvas.configure(scrollregion=self.canvas.bbox("all")) @@ -110,7 +101,21 @@ def update_settings_panel(self): self.pnl_settings.grid(row=0, column=0, sticky="ew") self.pnl_settings.on_preview_clicked = self.pnl_settings_on_preview_clicked self.pnl_settings.on_write_clicked = self.pnl_settings_on_write_clicked - + + def update_estimation_panel(self): + self.pnl_estimation.destroy() + self.inst_info = InstrumentInfo(settings=self.settings) + self.pnl_estimation = EstimationPanel(self, estimation=self.estimation,settings=self.settings) + self.pnl_estimation.grid(row=1, column=0, sticky="ew") + self.pnl_estimation.on_preview_clicked = self.pnl_settings_on_preview_clicked + self.pnl_estimation.on_write_clicked = self.pnl_settings_on_write_clicked + self.pnl_estimation.on_calculate_clicked = self.pnl_estimate_on_calculate_clicked + + def update_panel(self): + self.update_settings_panel() + self.update_estimation_panel() + + def load_last_instrument(self): inst = self.settings.last_instrument # Replace with actual settings retrieval self.settings.experiment.is_instrument = inst @@ -142,7 +147,7 @@ def pnl_settings_on_preview_clicked(self): def pnl_estimate_on_calculate_clicked(self): constructor = TableDataConstructor(TableEntries) self.data = constructor.create_experiments(self.data_frame.data.values()) - self.estimation.script_time = self.script_generator.estimate_run_time(self.data.runs, self.settings.experiment, self.estimation) + self.estimation.script_time = self.script_generator.estimate_run_time(self.data, self.settings.experiment, self.estimation) def btn_create_new_table_click(self): self.data.clear() diff --git a/Models/A2Setting.py b/Models/A2Setting.py index 0921902..9533cac 100644 --- a/Models/A2Setting.py +++ b/Models/A2Setting.py @@ -45,4 +45,8 @@ def get_ZOOM_choices(): # Example usage if __name__ == "__main__": - print(A2Setting.get_LOQ_choices()) \ No newline at end of file + print(A2Setting.get_LOQ_choices()[0]) + + + + diff --git a/Models/Estimation.py b/Models/Estimation.py index 53b1689..2f99e71 100644 --- a/Models/Estimation.py +++ b/Models/Estimation.py @@ -1,14 +1,14 @@ import tkinter as tk - +from Properties.Settings import Settings class Estimation: - def __init__(self, count_rate=40, move_time=120, script_time = 0): - - self._count_rate = tk.DoubleVar(value=count_rate) - self._move_time = tk.DoubleVar(value=move_time) - self._script_time = tk.DoubleVar(value=script_time) + def __init__(self, settings=None): + self.settings = settings if settings is not None else Settings() + self._count_rate = tk.DoubleVar(value=self.settings.count_rate) + self._move_time = tk.DoubleVar(value=self.settings.move_time) + self._script_time = tk.DoubleVar(value=0) self._is_valid = tk.BooleanVar(value=True) @@ -18,6 +18,15 @@ def __init__(self, count_rate=40, move_time=120, script_time = 0): self._count_rate.trace_add("write", self.validate) self._move_time.trace_add("write", self.validate) self._script_time.trace_add("write", self.validate) + + self._count_rate.trace_add("write", self.on_property_changed) + self._move_time.trace_add("write", self.on_property_changed) + + self._count_rate.trace_add("write", self.save_changes) + self._move_time.trace_add("write", self.save_changes) + + + @property def count_rate(self): @@ -55,9 +64,13 @@ def is_valid(self, value): self._is_valid.set(value) self.on_property_changed("is_valid") - def on_property_changed(self, name): + def on_property_changed(self, *args): for callback in self.property_changed: - callback(name) + callback(args[0]) + + def save_changes(self, *args): + self.settings.count_rate = self.count_rate + self.settings.move_time = self.move_time def validate(self, *args): error_message = "" @@ -74,6 +87,12 @@ def validate(self, *args): return error_message + def update_count_rate(self): + self._count_rate.set(self.settings.count_rate) + + def update_move_time(self): + self._move_time.set(self.settings.move_time) + # Example usage if __name__ == "__main__": def on_property_changed(name): diff --git a/Models/ExperimentSettings.py b/Models/ExperimentSettings.py index 9e2ca1e..1b08735 100644 --- a/Models/ExperimentSettings.py +++ b/Models/ExperimentSettings.py @@ -11,7 +11,7 @@ class ExperimentSettings: def __init__(self, settings=None, estimation=None): self.settings = settings if settings is not None else Settings() - self.estimation = estimation if estimation is not None else Estimation() + self.estimation = estimation if estimation is not None else Estimation(settings=self.settings) self._order = tk.StringVar(value=RunOrder.get_choice_string(self.settings.run_order)) @@ -44,6 +44,24 @@ def __init__(self, settings=None, estimation=None): self._sample_width.trace_add("write", self.on_property_changed) self._collection_mode.trace_add("write", self.on_property_changed) + self._num_sans.trace_add("write", self.validate) + self._num_trans.trace_add("write", self.validate) + self._sample_height.trace_add("write", self.validate) + self._sample_width.trace_add("write", self.validate) + self._loop_per_run.trace_add("write", self.validate) + + + self._order.trace_add("write", self.save_changes) + self._num_sans.trace_add("write", self.save_changes) + self._num_trans.trace_add("write", self.save_changes) + self._loop_per_run.trace_add("write", self.save_changes) + self._a2_setting_sans.trace_add("write", self.save_changes) + self._a2_setting_trans.trace_add("write", self.save_changes) + self._sample_geometry.trace_add("write", self.save_changes) + self._sample_height.trace_add("write", self.save_changes) + self._sample_width.trace_add("write", self.save_changes) + self._collection_mode.trace_add("write", self.save_changes) + self.larmor_aperture_sizes=A2Setting.get_LARMOR_choices() self.loq_aperture_sizes=A2Setting.get_LOQ_choices() self.sans2d_aperture_sizes=A2Setting.get_SANS2D_choices() @@ -87,7 +105,8 @@ def __call__(self, order=None, num_sans=None, num_trans=None, loop_per_run=None, def on_property_changed(self, *args): for callback in self.property_changed: callback(args[0]) - self.settings.save() + + def update_instrument(self, *args): if self.is_instrument == "LARMOR": @@ -98,23 +117,31 @@ def update_instrument(self, *args): self.set_instrument(self.is_instrument, "A6 Setting:", self.sans2d_aperture_sizes,"Data Acquisition:",self.sans2d_data_collection, 40) elif self.is_instrument == "ZOOM": self.set_instrument(self.is_instrument, "A6 Setting:", self.zoom_aperture_sizes,"Data Acquisition:",self.zoom_data_collection, 40) + + + def set_instrument(self, instrument, a2_label, a2_choices, data_collection_label, data_collection_choices, count_rate): + + self.settings.last_instrument = instrument + self.estimation.count_rate = count_rate + self.count_rate = count_rate + self.settings.count_rate = count_rate + - def set_instrument(self, instrument, a2_label, a2_choices, data_collection_label, data_collection_choices, count_rate): - - - self.settings.is_instrument = instrument - self.a2_label = a2_label self.a2_choices = a2_choices self.data_collection_label = data_collection_label self.data_collection_choices = data_collection_choices self.a2_trans_enabled = True self.collection_mode_enabled = True - self.estimation.count_rate = count_rate + + # set to default 1st entry in choices + self.settings.a2_setting_sans = a2_choices[0] + self.settings.a2_setting_trans = a2_choices[0] + self.settings.collection_mode = 'Events' - ''' - def save(self): + + def save_changes(self, *args): self.settings.run_order = self.order self.settings.num_sans = self.num_sans self.settings.num_trans = self.num_trans @@ -125,8 +152,7 @@ def save(self): self.settings.sample_height = self.sample_height self.settings.sample_width = self.sample_width self.settings.collection_mode = self.collection_mode - self.settings.save() - ''' + @property def order(self): @@ -237,8 +263,10 @@ def is_valid(self): def is_valid(self, value): self._is_valid.set(value) - def validate(self): + def validate(self, *args): error_message = "" + if self.loop_per_run < 0: + error_message = "Number of Loops per runs cannot be negative!" if self.num_sans < 0: error_message = "Number of SANS runs cannot be negative!" elif self.num_trans < 0: diff --git a/Models/Geometry.py b/Models/Geometry.py index 78ca559..b8c2269 100644 --- a/Models/Geometry.py +++ b/Models/Geometry.py @@ -30,6 +30,13 @@ def get_choice_string(ordinal): return choice[1] return None + @staticmethod + def get_ordinal_from_string(choice_string): + for choice in Geometry.list_order: + if choice[1] == choice_string: + return choice[0] + return None + # Example usage if __name__ == "__main__": print(Geometry.get_choices()) diff --git a/Models/RunOrder.py b/Models/RunOrder.py index 400382f..a05cd41 100644 --- a/Models/RunOrder.py +++ b/Models/RunOrder.py @@ -29,7 +29,15 @@ def get_choice_string(ordinal): return choice[1] return None + @staticmethod + def get_ordinal_from_string(choice_string): + for choice in RunOrder.list_order: + if choice[1] == choice_string: + return choice[0] + return None + # Example usage if __name__ == "__main__": print(RunOrder.get_choices()) - print(RunOrder.get_choice_string(1)) \ No newline at end of file + print(RunOrder.get_choice_string(1)) + print(RunOrder.get_ordinal_from_string("1: All TRANS first")) \ No newline at end of file diff --git a/Properties/Settings.ini b/Properties/Settings.ini index 3ddacf2..8c9537d 100644 --- a/Properties/Settings.ini +++ b/Properties/Settings.ini @@ -1,16 +1,16 @@ [DEFAULT] -lastinstrument = LARMOR -sampleheight = 10.0 -samplewidth = 10.0 -geometry = 3 -runorder = 1 -numtrans = 1 +lastinstrument = LOQ +sampleheight = 12 +samplewidth = 12 +geometry = 4 +runorder = 3 +numtrans = 5 numsans = 1 -loopperrun = False -a2settingsans = Large -a2settingtrans = Large +loopperrun = True +a2settingsans = Large = 17 mm +a2settingtrans = Large = 17 mm collectionmode = Events tableentries = "POSITION, TRANS, TRANS_WAIT, SANS, SANS_WAIT, PERIOD, SAMPLE_IDENTIFICATION_STRING, BACKGROUND, THICKNESS, TEMPERATURE, TEMPERATURE2, FIELD, SHEAR_RATE_1, SHEAR_RATE_2, SHEAR_ANGLE_1, SHEAR_ANGLE_2, PRE_COMMAND, POST_COMMAND, RB_NUMBER" -countrate = 38 -movetime = 120 +countrate = 170 +movetime = 60.0 diff --git a/Scripting/ScriptGenerator.py b/Scripting/ScriptGenerator.py index c4891c2..4e2649f 100644 --- a/Scripting/ScriptGenerator.py +++ b/Scripting/ScriptGenerator.py @@ -65,7 +65,7 @@ def generate_runs(self, runs, settings): if not runs: return script - if float(settings.order) == RunOrder.ALL_TRANS[0]: + if float(settings.settings.run_order) == RunOrder.ALL_TRANS[0]: script += self.get_indent_string(1) + f"for i in range({settings.num_trans}):\n" for exp in runs: if exp.trans: @@ -74,7 +74,7 @@ def generate_runs(self, runs, settings): for exp in runs: if exp.sans: script = self.do_sans_run(script, exp, 2, settings) - elif float(settings.order) == RunOrder.ALL_SANS[0]: + elif float(settings.settings.run_order) == RunOrder.ALL_SANS[0]: script += self.get_indent_string(1) + f"for i in range({settings.num_sans}):\n" for exp in runs: if exp.sans: @@ -91,7 +91,7 @@ def generate_runs(self, runs, settings): script += self.get_indent_string(1) + "while True:\n" indent = 2 for exp in runs: - if float(settings.order) == RunOrder.TRANS_FIRST[0]: + if float(settings.settings.run_order) == RunOrder.TRANS_FIRST[0]: if exp.trans: script += self.get_indent_string(indent) + "if count < num_trans:\n" script = self.do_trans_run(script, exp, indent + 1, settings) @@ -247,7 +247,7 @@ def get_minutes(wait, length, count_rate): experiment_settings =ExperimentSettings() settings = experiment_settings(sample_width="10", sample_height="10", sample_geometry="Flat", order="AllTrans", num_trans=1, num_sans=1, collection_mode="Events", a2_setting_trans="Large", a2_setting_sans="Large") estimation=Estimation() - estimate = estimation(count_rate=100, move_time=10) + estimate = estimation(settings=settings) instrument = "LARMOR" generator = ScriptGenerator(instrument) script = generator.generate_script(runs, settings, "my_script") diff --git a/SettingsPanel.py b/SettingsPanel.py index 20974c4..9924c15 100644 --- a/SettingsPanel.py +++ b/SettingsPanel.py @@ -1,7 +1,5 @@ import tkinter as tk from tkinter import ttk -#from ViewModels.SettingsPanelVM import SettingsPanelVM -#from ViewModels.InstrumentVM import InstrumentVM from Properties.Settings import Settings from Models.RunOrder import RunOrder from Models.Geometry import Geometry @@ -15,8 +13,6 @@ def __init__(self, parent,inst_info=None, settings=None): self.parent = parent self.settings = settings if settings is not None else Settings() self.inst_info = inst_info if inst_info is not None else InstrumentInfo() - #self.settings_vm = settings_vm if settings_vm is not None else SettingsPanelVM() - #self.instrument_vm = instrument_vm if instrument_vm is not None else InstrumentVM() self.on_preview_clicked = None self.on_write_clicked = None self.property_changed = [] @@ -49,17 +45,8 @@ def create_widgets(self): self.com_order["values"]=self.order_choices self.com_order.grid(row=0, column=1, columnspan=2, padx=5, pady=5) self.com_order.set(RunOrder.get_choice_string(self.settings.run_order)) # Set default value - ''' - # Collection Mode - ttk.Label(grid_frame, text="Collection Mode:").grid(row=1, column=0, sticky=tk.E) - print(self.settings_vm.collection_mode_choices) - print(settings.collection_mode) - - self.collect_mode = ttk.Combobox(grid_frame, values=self.settings_vm.collection_mode_choices) - self.collect_mode["values"]=self.settings_vm.collection_mode_choices - self.collect_mode.grid(row=1, column=1, columnspan=2, padx=5, pady=5) - self.collect_mode.set(settings.collection_mode) # Set default value - ''' + self.com_order.bind("", self.update_order) + # Do SANS ttk.Label(grid_frame, text="Do SANS:").grid(row=1, column=0, sticky=tk.E) @@ -69,7 +56,8 @@ def create_widgets(self): self.txt_times_sans.insert(0, self.settings.num_sans) # Default value self.txt_times_sans.grid(row=0, column=0, padx=5, pady=5) ttk.Label(sans_frame, text="time(s)").grid(row=0, column=1) - + self.txt_times_sans.bind("", self.update_num_sans) + # Do TRANS ttk.Label(grid_frame, text="Do TRANS:").grid(row=2, column=0, sticky=tk.E) trans_frame = ttk.Frame(grid_frame) @@ -78,17 +66,20 @@ def create_widgets(self): self.txt_times_trans.insert(0, self.settings.num_trans) # Default value self.txt_times_trans.grid(row=0, column=0, padx=5, pady=5) ttk.Label(trans_frame, text="time(s)").grid(row=0, column=1) + self.txt_times_trans.bind("", self.update_num_trans) # Loop over each run self.loop_run_var = tk.BooleanVar(value=self.settings.loop_per_run) # Create BooleanVar and set default value self.chk_loop_run = ttk.Checkbutton(grid_frame, text="Loop over each run?", variable=self.loop_run_var) self.chk_loop_run.grid(row=3, column=0, columnspan=3, pady=5) + self.chk_loop_run.bind("", self.update_loop_per_run) # Collection Mode tk.Label(grid_frame, text=self.inst_info.data_collection_label).grid(row=4, column=0, sticky=tk.E) self.com_collection_mode = ttk.Combobox(grid_frame, values=self.inst_info.data_collection_choices) self.com_collection_mode.grid(row=4, column=1, columnspan=2, padx=5, pady=5) self.com_collection_mode.set(self.settings.collection_mode) # Set default value + self.com_collection_mode.bind("", self.update_collection_mode) # A2 SANS @@ -96,19 +87,21 @@ def create_widgets(self): self.com_a2_sans = ttk.Combobox(grid_frame, values=self.inst_info.a2_choices ) self.com_a2_sans.grid(row=5, column=1, columnspan=2, padx=5, pady=5) self.com_a2_sans.set(self.settings.a2_setting_sans) # Set default value + self.com_a2_sans.bind("", self.update_a2_sans) # A2 TRANS tk.Label(grid_frame, text="TRANS "+self.inst_info.a2_label).grid(row=6, column=0, sticky=tk.E) self.com_a2_trans = ttk.Combobox(grid_frame, values=self.inst_info.a2_choices) self.com_a2_trans.grid(row=6, column=1, columnspan=2, padx=5, pady=5) self.com_a2_trans.set(self.settings.a2_setting_trans) # Set default value + self.com_a2_trans.bind("", self.update_a2_trans) # Sample Geometry ttk.Label(grid_frame, text="Sample Geometry:").grid(row=7, column=0, sticky=tk.E) self.com_sample_geo = ttk.Combobox(grid_frame, values=self.geometry_choices) self.com_sample_geo.grid(row=7, column=1, columnspan=2, padx=5, pady=5) self.com_sample_geo.set(Geometry.get_choice_string(self.settings.geometry)) # Set default value - + self.com_sample_geo.bind("", self.update_sample_geometry) # Sample Height ttk.Label(grid_frame, text="Sample Height:").grid(row=8, column=0, sticky=tk.E) @@ -118,6 +111,7 @@ def create_widgets(self): self.txt_sample_height.insert(0, self.settings.sample_height) # Default value self.txt_sample_height.grid(row=0, column=0, padx=5, pady=5) ttk.Label(sample_height_frame, text="mm").grid(row=0, column=1) + self.txt_sample_height.bind("", self.update_sample_height) # Sample Width ttk.Label(grid_frame, text="Sample Width:").grid(row=9, column=0, sticky=tk.E) @@ -127,6 +121,7 @@ def create_widgets(self): self.txt_sample_width.insert(0, self.settings.sample_width) # Default value self.txt_sample_width.grid(row=0, column=0, padx=5, pady=5) ttk.Label(sample_width_frame, text="mm").grid(row=0, column=1) + self.txt_sample_width.bind("", self.update_sample_width) # Buttons button_frame = ttk.Frame(self) @@ -136,6 +131,42 @@ def create_widgets(self): self.btn_write_script = ttk.Button(button_frame, text="Write Script", command=self.btn_write_script_click) self.btn_write_script.grid(row=0, column=1, padx=5) + # Define the Settings update methods + def update_order(self, event): + self.settings.run_order = RunOrder.get_ordinal_from_string(self.com_order.get()) + + def update_num_sans(self, event): + self.settings.num_sans = self.txt_times_sans.get() + + def update_num_trans(self, event): + self.settings.num_trans = self.txt_times_trans.get() + + def update_loop_per_run(self, event): + self.settings.loop_per_run = self.loop_run_var.get() + + def update_collection_mode(self, event): + self.settings.collection_mode = self.com_collection_mode.get() + + def update_collection_mode(self, event): + self.settings.collection_mode = self.com_collection_mode.get() + + def update_a2_sans(self, event): + self.settings.a2_setting_sans = self.com_a2_sans.get() + + def update_a2_trans(self, event): + self.settings.a2_setting_trans = self.com_a2_trans.get() + + def update_sample_geometry(self, event): + self.settings.geometry = Geometry.get_ordinal_from_string(self.com_sample_geo.get()) + + def update_sample_height(self, event): + self.settings.sample_height = self.txt_sample_height.get() + + def update_sample_width(self, event): + self.settings.sample_width = self.txt_sample_width.get() + + + def btn_preview_script_click(self): if self.on_preview_clicked: self.on_preview_clicked() diff --git a/ViewModels/DataGridVM.py b/ViewModels/DataGridVM.py deleted file mode 100644 index 0b9dfc7..0000000 --- a/ViewModels/DataGridVM.py +++ /dev/null @@ -1,47 +0,0 @@ -import tkinter as tk -from tkinter import StringVar -from tkinter import ttk -from Models.WaitForUnits import WaitForUnits -from Models.Experiment import Experiment - - - -class DataGridVM: - def __init__(self): - self._runs = tk.Variable(value=[]) - self.property_changed = [] - - @property - def runs(self): - return self._runs.get() - - @runs.setter - def runs(self, value): - self._runs.set(value) - self.on_property_changed("runs") - - @staticmethod - def wait_for_choices(): - return WaitForUnits.choices() - - def on_property_changed(self, name): - for callback in self.property_changed: - callback(name) - - - - - -if __name__ == "__main__": - root = tk.Tk() - vm = DataGridVM() - - def on_property_changed(name): - print(f"Property {name} changed") - - vm.property_changed.append(on_property_changed) - - vm.runs = [Experiment(), Experiment()] - print(vm.runs) - - root.mainloop() \ No newline at end of file diff --git a/ViewModels/EstimationPanelVM.py b/ViewModels/EstimationPanelVM.py deleted file mode 100644 index d9e2c76..0000000 --- a/ViewModels/EstimationPanelVM.py +++ /dev/null @@ -1,28 +0,0 @@ -import tkinter as tk - - - -class EstimationPanelVM: - def __init__(self): - self.property_changed = [] - self.count_rate = tk.StringVar() - self.move_time = tk.StringVar() - self.script_time = tk.StringVar() - - def on_property_changed(self, name): - for callback in self.property_changed: - callback(name) - -if __name__ == "__main__": - root = tk.Tk() - vm = EstimationPanelVM() - - def on_property_changed(name): - print(f"Property {name} changed") - - vm.property_changed.append(on_property_changed) - - # Example of triggering property change - vm.on_property_changed("example_property") - - root.mainloop() \ No newline at end of file diff --git a/ViewModels/SettingsPanelVM.py b/ViewModels/SettingsPanelVM.py deleted file mode 100644 index 51fd0c9..0000000 --- a/ViewModels/SettingsPanelVM.py +++ /dev/null @@ -1,76 +0,0 @@ -import tkinter as tk -from tkinter import StringVar -from Models.Estimation import Estimation -from Models.Experiment import Experiment -from Models.ExperimentSettings import ExperimentSettings -from Models.InstrumentInfo import InstrumentInfo -from Models.DataCollection import DataCollection -from Models.RunOrder import RunOrder -from Models.Geometry import Geometry - -class SettingsPanelVM: - def __init__(self, inst_info=None, experiment=None): - self.property_changed = [] - self.order_choices = RunOrder.get_choices() - self.geometry_choices = Geometry.get_choices() - #self.collection_mode_choices = DataCollection.get_choices() - - self._experiment = experiment if experiment is not None else ExperimentSettings() - self._inst_info = inst_info if inst_info is not None else InstrumentInfo() - - def on_property_changed(self, name): - for callback in self.property_changed: - callback(name) - - @property - def experiment(self): - return self._experiment - - @experiment.setter - def experiment(self, value): - self._experiment = value - self.on_property_changed("experiment") - - @property - def inst_info(self): - return self._inst_info - - @inst_info.setter - def inst_info(self, value): - self._inst_info = value - self.on_property_changed("inst_info") - - @staticmethod - def order_choices(): - return RunOrder.get_choices() - - @staticmethod - def geometry_choices(): - return Geometry.get_choices() - ''' - @staticmethod - def collection_mode_choices(): - return DataCollection.get_choices() - ''' - - - - - - - - - -if __name__ == "__main__": - root = tk.Tk() - vm = SettingsPanelVM() - - def on_property_changed(name): - print(f"Property {name} changed") - - vm.property_changed.append(on_property_changed) - - vm.experiment = ExperimentSettings() - vm.inst_info = InstrumentInfo() - - root.mainloop() \ No newline at end of file diff --git a/ViewModels/WaitForUnits.py b/ViewModels/WaitForUnits.py deleted file mode 100644 index 69c86ba..0000000 --- a/ViewModels/WaitForUnits.py +++ /dev/null @@ -1,4 +0,0 @@ -class WaitForUnits: - @staticmethod - def choices(): - return ["Choice1", "Choice2", "Choice3"] \ No newline at end of file From 0ac964236fcff121ac68e19bfe696f38da40792e Mon Sep 17 00:00:00 2001 From: Dirk Honecker Date: Mon, 6 Jan 2025 00:06:43 +0000 Subject: [PATCH 68/88] table size changes with window --- App.py | 4 +- Dialogs/PreviewDialog.py | 4 +- EstimationPanel.py | 12 ++-- MainWindow.py | 52 ++++++++++----- TableWidget.py | 137 ++++++++++++++++++++++++++++++++++++--- 5 files changed, 174 insertions(+), 35 deletions(-) diff --git a/App.py b/App.py index c0f82eb..41ee35e 100644 --- a/App.py +++ b/App.py @@ -6,8 +6,8 @@ def __init__(self): self.root = tk.Tk() self.main_window = MainWindow(self.root) self.main_window.grid(sticky="nsew") - self.root.grid_rowconfigure(1, weight=1) - self.root.grid_columnconfigure(1, weight=1) + self.root.grid_rowconfigure(0, weight=1) + self.root.grid_columnconfigure(0, weight=1) self.root.protocol("WM_DELETE_WINDOW", self.on_close) diff --git a/Dialogs/PreviewDialog.py b/Dialogs/PreviewDialog.py index 3b03c45..c598cac 100644 --- a/Dialogs/PreviewDialog.py +++ b/Dialogs/PreviewDialog.py @@ -16,7 +16,7 @@ def __init__(self, parent, script=None): # Create a frame for the text area with a border border_frame = tk.Frame(self, borderwidth=1, relief="solid") - border_frame.grid(row=0, column=0, padx=5, pady=5, sticky="nsew") + border_frame.grid(row=0, column=0, sticky="nsew") # Create a scrollable text area self.txt_script = scrolledtext.ScrolledText(border_frame, wrap=tk.WORD) @@ -26,7 +26,7 @@ def __init__(self, parent, script=None): self.txt_script.config(state=tk.DISABLED) # Create a frame for the buttons button_frame = tk.Frame(self) - button_frame.grid(row=1, column=0, pady=5, sticky="e") + button_frame.grid(row=1, column=0, sticky="e") # Create the Print Script button (initially hidden) self.btn_print = tk.Button(button_frame, text="Print Script", command=self.print_script) diff --git a/EstimationPanel.py b/EstimationPanel.py index 8898781..e49de9b 100644 --- a/EstimationPanel.py +++ b/EstimationPanel.py @@ -18,10 +18,10 @@ def create_widgets(self): self.grid(sticky="nsew") label = ttk.Label(self, text="Estimated Script Time", font=("Helvetica", 16, "bold")) - label.grid(row=0, column=0, columnspan=3, pady=10) + label.grid(row=0, column=0, columnspan=3) grid_frame = ttk.Frame(self) - grid_frame.grid(row=1, column=0, columnspan=3, padx=10, pady=10, sticky="nsew") + grid_frame.grid(row=1, column=0, columnspan=3, sticky="nsew") # Est. count rate @@ -30,7 +30,7 @@ def create_widgets(self): count_rate_frame.grid(row=0, column=1, columnspan=2) self.txt_count_rate = ttk.Entry(count_rate_frame, width=10) self.txt_count_rate.insert(0, self.settings.count_rate) # Default value - self.txt_count_rate.grid(row=0, column=0, padx=5, pady=5) + self.txt_count_rate.grid(row=0, column=0) ttk.Label(count_rate_frame, text="µA/hr").grid(row=0, column=1) self.txt_count_rate.bind("", self.update_count_rate) @@ -40,7 +40,7 @@ def create_widgets(self): move_time_frame.grid(row=1, column=1, columnspan=2) self.txt_move_time = ttk.Entry(move_time_frame, width=10) self.txt_move_time.insert(0, self.settings.move_time) # Default value - self.txt_move_time.grid(row=0, column=0, padx=5, pady=5) + self.txt_move_time.grid(row=0, column=0) ttk.Label(move_time_frame, text="seconds").grid(row=0, column=1) self.txt_move_time.bind("", self.update_move_time) @@ -51,11 +51,11 @@ def create_widgets(self): script_time_frame = ttk.Frame(grid_frame) script_time_frame.grid(row=2, column=1, columnspan=2) self.txt_estimation = ttk.Entry(script_time_frame, width=15, textvariable=self.estimation.script_time, state="readonly") - self.txt_estimation.grid(row=0, column=0, padx=5, pady=5) + self.txt_estimation.grid(row=0, column=0) # Calculate button self.btn_calculate = ttk.Button(grid_frame, text="Calculate", command=self.btn_calculate_click) - self.btn_calculate.grid(row=3, column=1, columnspan=2, pady=10) + self.btn_calculate.grid(row=3, column=1, columnspan=2) # Define the Settings update methods diff --git a/MainWindow.py b/MainWindow.py index 6754adf..60607fa 100644 --- a/MainWindow.py +++ b/MainWindow.py @@ -23,7 +23,8 @@ def __init__(self,parent): super().__init__(parent) self.parent = parent self.parent.title("SANS Script GUI") - self.parent.geometry("800x600") + #self.parent.geometry("1200x600") + self.parent.resizable(True, True) self.settings = Settings() @@ -33,26 +34,41 @@ def __init__(self,parent): self.create_widgets() self.load_last_instrument() - + def create_widgets(self): #self.data_grid = ttk.Treeview(self) - self.grid_columnconfigure(1, weight=1) - self.grid_rowconfigure(1, weight=1) - - + + # Configure grid weights for the parent container + + #self.parent.rowconfigure(0,weight=1) + self.parent.columnconfigure(0,weight=1) + #self.master.rowconfigure(0,weight=1) + self.master.columnconfigure(0,weight=1) + # Configure grid weights for the frame itself + #self.grid_rowconfigure(0, weight=1) + #self.grid_columnconfigure(0, weight=1) + #self.rowconfigure(0, weight=1) + #self.columnconfigure(0, weight=1) + + # Data Grid with horizontal and vertical scrollbars - self.data_frame = TableWidget(self) - #self.data_frame.grid(row=0, column=1, rowspan=4, sticky="nsew", padx=5, pady=5) - self.data_frame.grid(row=0, column=1, sticky="nsew", padx=5, pady=5) - + self.data_frame = TableWidget(self) + self.data_frame.grid(row=0, column=1, rowspan=3, sticky="nsew") + + #self.data_frame.config(width=800) + + + self.columnconfigure(1, weight=1) + """ for col in self.data_grid["columns"]: self.data_grid.heading(col, text=col.replace("_", " ")) """ + # Settings Panel self.pnl_settings = SettingsPanel(self, inst_info=self.inst_info,settings=self.settings) self.pnl_settings.grid(row=0, column=0, sticky="ew") @@ -64,12 +80,12 @@ def create_widgets(self): self.pnl_estimation.on_calculate_clicked = self.pnl_estimate_on_calculate_clicked # Buttons button_frame = ttk.Frame(self) - button_frame.grid(row=2, column=0, padx=5, pady=5, sticky="w") + button_frame.grid(row=2, column=0, sticky="w") self.btn_create_new_table = ttk.Button(button_frame, text="Create New Table", command=self.btn_create_new_table_click) - self.btn_create_new_table.grid(row=0, column=0, padx=5, pady=5) + self.btn_create_new_table.grid(row=0, column=0 ) self.btn_open_excel = ttk.Button(button_frame, text="Open Excel", command=self.btn_open_excel_click) - self.btn_open_excel.grid(row=0, column=1, padx=5, pady=5) + self.btn_open_excel.grid(row=0, column=1) self.mnu_main = tk.Menu(self.parent) @@ -89,6 +105,8 @@ def create_widgets(self): instrument_menu.add_radiobutton(label="LOQ", variable=self.settings.experiment.instrument, value="LOQ", command=self.update_panel) instrument_menu.add_radiobutton(label="SANS2D", variable=self.settings.experiment.instrument, value="SANS2D", command=self.update_panel) instrument_menu.add_radiobutton(label="ZOOM", variable=self.settings.experiment.instrument, value="ZOOM", command=self.update_panel) + + """ def onFrameConfigure(self, event): self.canvas.configure(scrollregion=self.canvas.bbox("all")) @@ -182,12 +200,12 @@ def mnu_save_csv_click(self): CsvExporter.export(filepath, self.data) except Exception as err: messagebox.showerror("SANS Script - Error", "Could not save as CSV") - + def on_close(self): # Save last_instrument to settings self.settings.save() self.parent.destroy() - + @@ -197,7 +215,7 @@ def on_close(self): root = tk.Tk() app = MainWindow(root) app.grid(sticky="nsew") - root.grid_rowconfigure(0, weight=1) - root.grid_columnconfigure(0, weight=1) + #root.grid_rowconfigure(0, weight=1) + #root.grid_columnconfigure(0, weight=1) app.protocol("WM_DELETE_WINDOW", app.on_close) app.mainloop() \ No newline at end of file diff --git a/TableWidget.py b/TableWidget.py index 132309f..9496e8e 100644 --- a/TableWidget.py +++ b/TableWidget.py @@ -112,9 +112,27 @@ def show(self, callback=None): self.parentframe.rowconfigure(1,weight=1) self.parentframe.columnconfigure(1,weight=1) - self.tablecolheader.grid(row=0,column=1,rowspan=1,sticky="news",pady=0,ipady=0) - self.tablerowheader.grid(row=1,column=0,rowspan=1,sticky="news",pady=0,ipady=0) - self.grid(row=1,column=1,rowspan=1,sticky="news",pady=0,ipady=0) + # Configure grid layout + ''' + #self.parentframe.grid_rowconfigure(0, weight=0) + #self.parentframe.grid_rowconfigure(1, weight=1) + #self.parentframe.grid_rowconfigure(2, weight=0) + + #self.parentframe.grid_columnconfigure(0, weight=0) + #self.parentframe.grid_columnconfigure(1, weight=1) + #self.parentframe.grid_columnconfigure(2, weight=0) + #self.parentframe.grid_columnconfigure(3, weight=0) + ''' + + self.tablecolheader.grid(row=0,column=1,rowspan=1,sticky='news',pady=0,ipady=0) + self.tablerowheader.grid(row=1,column=0,rowspan=1,sticky='news',pady=0,ipady=0) + #self.tablecolheader.grid(row=0, column=1, sticky="ew") + #self.tablerowheader.grid(row=1, column=0, sticky="ns") + self.grid(row=1, column=1, sticky="news",pady=0,ipady=0) + #self.Yscrollbar.grid(row=1, column=2, sticky="ns") + #self.Xscrollbar.grid(row=2, column=1, sticky="ew") + + #self.grid(row=1,column=1,rowspan=1,sticky="news",pady=0,ipady=0) self.adjustColumnWidths() self.redrawTable(callback=callback) @@ -122,18 +140,114 @@ def show(self, callback=None): self.tablecolheader.xview("moveto", 0) self.xview("moveto", 0) return - + ''' + def on_resize(self, event): + self.update_table_size() + + def update_table_size(self): + """Adjust the table width based on the available space in the parent frame.""" + available_width= self.parentframe.winfo_width() + if available_width <= 1.0: + available_width = self.parentframe.master.winfo_width() + print(available_width) + required_width = (self.cellwidth) * self.cols + self.tablewidth = max(available_width, required_width) + self.redrawTable() + ''' + ''' def updateModel(self, model): """Call this method to update the table model""" self.model = model self.rows = self.model.getRowCount() self.cols = self.model.getColumnCount() - self.tablewidth = (self.cellwidth)*self.cols + if self.tablewidth is None: + self.tablewidth = (self.cellwidth)*self.cols self.tablecolheader = CustomColumnHeader(self.parentframe, self) self.tablerowheader = CustomRowHeader(self.parentframe, self) self.createTableFrame() return + ''' + ''' + def redrawTable(self, event=None, callback=None): + self.redrawVisible(event, callback) + return + ''' + ''' + def getVisibleRegion(self): + """Get visible region of table to display""" + + x1, y1 = self.canvasx(0), self.canvasy(0) + #w, h = self.winfo_width(), self.winfo_height() + w,h= self.parentframe.winfo_width(), self.parentframe.winfo_height() + if w <= 1.0 or h <= 1.0: + w, h = self.parentframe.master.winfo_width(), self.parentframe.master.winfo_height() + x2, y2 = self.canvasx(w), self.canvasy(h) + return x1, y1, x2, y2 + + def redrawVisible(self, event=None, callback=None): + """Redraw the visible portion of the canvas""" + + model = self.model + self.rows = self.model.getRowCount() + self.cols = self.model.getColumnCount() + if self.tablewidth is None: + self.tablewidth = (self.cellwidth) * self.cols + self.configure(bg=self.bgcolor) + self.setColPositions() + + #are we drawing a filtered subset of the recs? + if self.filtered == True and self.model.filteredrecs != None: + self.rows = len(self.model.filteredrecs) + self.delete('colrect') + + self.rowrange = range(0,self.rows) + self.configure(scrollregion=(0,0, self.tablewidth+self.x_start, + self.rowheight*self.rows+10)) + + x1, y1, x2, y2 = self.getVisibleRegion() + startvisiblerow, endvisiblerow = self.getVisibleRows(y1, y2) + self.visiblerows = range(startvisiblerow, endvisiblerow) + startvisiblecol, endvisiblecol = self.getVisibleCols(x1, x2) + self.visiblecols = range(startvisiblecol, endvisiblecol) + + if self.cols == 0 or self.rows == 0: + self.delete('entry') + self.delete('rowrect') + self.delete('currentrect') + self.delete('gridline','text') + self.tablerowheader.redraw() + return + + self.drawGrid(startvisiblerow, endvisiblerow) + align = self.align + self.delete('fillrect') + for row in self.visiblerows: + if callback != None: + callback() + for col in self.visiblecols: + colname = model.getColumnName(col) + bgcolor = model.getColorAt(row,col, 'bg') + fgcolor = model.getColorAt(row,col, 'fg') + text = model.getValueAt(row,col) + self.drawText(row, col, text, fgcolor, align) + if bgcolor != None: + self.drawRect(row,col, color=bgcolor) + + #self.drawSelectedCol() + self.tablecolheader.redraw() + self.tablerowheader.redraw(align=self.align, showkeys=self.showkeynamesinheader) + #self.setSelectedRow(self.currentrow) + self.drawSelectedRow() + self.drawSelectedRect(self.currentrow, self.currentcol) + #print self.multiplerowlist + + if len(self.multiplerowlist)>1: + self.tablerowheader.drawSelectedRows(self.multiplerowlist) + self.drawMultipleRows(self.multiplerowlist) + self.drawMultipleCells() + return + ''' def deleteCells(self, rows=None, cols=None): """Clear the cell contents""" @@ -351,11 +465,18 @@ def __init__(self, parent=None, data=None): print (self.table.model.columnNames) #table.model.data[1]["a"] = "XX" #table.model.setValueAt("YY",0,2) - self.table.show() + self.grid(row=0, column=0, sticky="nsew") + #self.parent.grid_rowconfigure(0, weight=1) + #self.parent.grid_columnconfigure(0, weight=1) + + # Bind the resize event to redraw the table + + #self.parent.bind("", self.on_resize) - - + #def on_resize(self, event): + # self.table.update_table_size() + def customOption(self): print("Custom option selected") return From 6e64bb83ba1072ea81f47e373d81d6e933298be5 Mon Sep 17 00:00:00 2001 From: Dirk Honecker Date: Mon, 6 Jan 2025 00:21:31 +0000 Subject: [PATCH 69/88] regular white space around elements --- Dialogs/PreviewDialog.py | 10 +++++----- EstimationPanel.py | 28 ++++++++++++++-------------- MainWindow.py | 14 +++++++------- SettingsPanel.py | 30 +++++++++++++++--------------- 4 files changed, 41 insertions(+), 41 deletions(-) diff --git a/Dialogs/PreviewDialog.py b/Dialogs/PreviewDialog.py index c598cac..f822d55 100644 --- a/Dialogs/PreviewDialog.py +++ b/Dialogs/PreviewDialog.py @@ -16,26 +16,26 @@ def __init__(self, parent, script=None): # Create a frame for the text area with a border border_frame = tk.Frame(self, borderwidth=1, relief="solid") - border_frame.grid(row=0, column=0, sticky="nsew") + border_frame.grid(row=0, column=0, sticky="nsew",padx=5, pady=5) # Create a scrollable text area self.txt_script = scrolledtext.ScrolledText(border_frame, wrap=tk.WORD) - self.txt_script.grid(row=0, column=0, sticky="nsew") + self.txt_script.grid(row=0, column=0, sticky="nsew",padx=5, pady=5) self.txt_script.insert(tk.END, script) self.txt_script.config(state=tk.DISABLED) # Create a frame for the buttons button_frame = tk.Frame(self) - button_frame.grid(row=1, column=0, sticky="e") + button_frame.grid(row=1, column=0, sticky="e",padx=5, pady=5) # Create the Print Script button (initially hidden) self.btn_print = tk.Button(button_frame, text="Print Script", command=self.print_script) - self.btn_print.grid(row=0, column=0, padx=5) + self.btn_print.grid(row=0, column=0, padx=5, pady=5) self.btn_print.pack_forget() # Hide the button initially # Create the Close button btn_close = tk.Button(button_frame, text="Close", command=self.close) - btn_close.grid(row=0, column=1, padx=5) + btn_close.grid(row=0, column=1, padx=5, pady=5) def set_script(self, script): self.txt_script.delete(1.0, tk.END) diff --git a/EstimationPanel.py b/EstimationPanel.py index e49de9b..6b02877 100644 --- a/EstimationPanel.py +++ b/EstimationPanel.py @@ -18,44 +18,44 @@ def create_widgets(self): self.grid(sticky="nsew") label = ttk.Label(self, text="Estimated Script Time", font=("Helvetica", 16, "bold")) - label.grid(row=0, column=0, columnspan=3) + label.grid(row=0, column=0, columnspan=3,padx=5, pady=5) grid_frame = ttk.Frame(self) - grid_frame.grid(row=1, column=0, columnspan=3, sticky="nsew") + grid_frame.grid(row=1, column=0, columnspan=3, sticky="nsew",padx=5, pady=5) # Est. count rate - ttk.Label(grid_frame, text="Est. count rate:").grid(row=0, column=0, sticky=tk.E) + ttk.Label(grid_frame, text="Est. count rate:").grid(row=0, column=0, sticky=tk.E,padx=5, pady=5) count_rate_frame = ttk.Frame(grid_frame) - count_rate_frame.grid(row=0, column=1, columnspan=2) + count_rate_frame.grid(row=0, column=1, columnspan=2,padx=5, pady=5) self.txt_count_rate = ttk.Entry(count_rate_frame, width=10) self.txt_count_rate.insert(0, self.settings.count_rate) # Default value - self.txt_count_rate.grid(row=0, column=0) - ttk.Label(count_rate_frame, text="µA/hr").grid(row=0, column=1) + self.txt_count_rate.grid(row=0, column=0,padx=5, pady=5) + ttk.Label(count_rate_frame, text="µA/hr").grid(row=0, column=1,padx=5, pady=5) self.txt_count_rate.bind("", self.update_count_rate) # Est. time between moves - ttk.Label(grid_frame, text="Est. time between moves:").grid(row=1, column=0, sticky=tk.E) + ttk.Label(grid_frame, text="Est. time between moves:").grid(row=1, column=0, sticky=tk.E,padx=5, pady=5) move_time_frame = ttk.Frame(grid_frame) - move_time_frame.grid(row=1, column=1, columnspan=2) + move_time_frame.grid(row=1, column=1, columnspan=2,padx=5, pady=5) self.txt_move_time = ttk.Entry(move_time_frame, width=10) self.txt_move_time.insert(0, self.settings.move_time) # Default value - self.txt_move_time.grid(row=0, column=0) - ttk.Label(move_time_frame, text="seconds").grid(row=0, column=1) + self.txt_move_time.grid(row=0, column=0,padx=5, pady=5) + ttk.Label(move_time_frame, text="seconds").grid(row=0, column=1,padx=5, pady=5) self.txt_move_time.bind("", self.update_move_time) # Est. script time - ttk.Label(grid_frame, text="Est. script time:").grid(row=2, column=0, sticky=tk.E) + ttk.Label(grid_frame, text="Est. script time:").grid(row=2, column=0, sticky=tk.E,padx=5, pady=5) script_time_frame = ttk.Frame(grid_frame) - script_time_frame.grid(row=2, column=1, columnspan=2) + script_time_frame.grid(row=2, column=1, columnspan=2,padx=5, pady=5) self.txt_estimation = ttk.Entry(script_time_frame, width=15, textvariable=self.estimation.script_time, state="readonly") - self.txt_estimation.grid(row=0, column=0) + self.txt_estimation.grid(row=0, column=0,padx=5, pady=5) # Calculate button self.btn_calculate = ttk.Button(grid_frame, text="Calculate", command=self.btn_calculate_click) - self.btn_calculate.grid(row=3, column=1, columnspan=2) + self.btn_calculate.grid(row=3, column=1, columnspan=2,padx=5, pady=5) # Define the Settings update methods diff --git a/MainWindow.py b/MainWindow.py index 60607fa..694ca40 100644 --- a/MainWindow.py +++ b/MainWindow.py @@ -71,21 +71,21 @@ def create_widgets(self): # Settings Panel self.pnl_settings = SettingsPanel(self, inst_info=self.inst_info,settings=self.settings) - self.pnl_settings.grid(row=0, column=0, sticky="ew") + self.pnl_settings.grid(row=0, column=0, sticky="ew",padx=5, pady=5) self.pnl_settings.on_preview_clicked = self.pnl_settings_on_preview_clicked self.pnl_settings.on_write_clicked = self.pnl_settings_on_write_clicked # Estimation Panel self.pnl_estimation = EstimationPanel(self, estimation=self.estimation,settings=self.settings) - self.pnl_estimation.grid(row=1, column=0, sticky="ew") + self.pnl_estimation.grid(row=1, column=0, sticky="ew",padx=5, pady=5) self.pnl_estimation.on_calculate_clicked = self.pnl_estimate_on_calculate_clicked # Buttons button_frame = ttk.Frame(self) - button_frame.grid(row=2, column=0, sticky="w") + button_frame.grid(row=2, column=0, sticky="w",padx=5, pady=5) self.btn_create_new_table = ttk.Button(button_frame, text="Create New Table", command=self.btn_create_new_table_click) - self.btn_create_new_table.grid(row=0, column=0 ) + self.btn_create_new_table.grid(row=0, column=0 ,padx=5, pady=5) self.btn_open_excel = ttk.Button(button_frame, text="Open Excel", command=self.btn_open_excel_click) - self.btn_open_excel.grid(row=0, column=1) + self.btn_open_excel.grid(row=0, column=1,padx=5, pady=5) self.mnu_main = tk.Menu(self.parent) @@ -116,7 +116,7 @@ def update_settings_panel(self): self.pnl_settings.destroy() self.inst_info = InstrumentInfo(settings=self.settings) self.pnl_settings = SettingsPanel(self, inst_info=self.inst_info, settings=self.settings) - self.pnl_settings.grid(row=0, column=0, sticky="ew") + self.pnl_settings.grid(row=0, column=0, sticky="ew",padx=5, pady=5) self.pnl_settings.on_preview_clicked = self.pnl_settings_on_preview_clicked self.pnl_settings.on_write_clicked = self.pnl_settings_on_write_clicked @@ -124,7 +124,7 @@ def update_estimation_panel(self): self.pnl_estimation.destroy() self.inst_info = InstrumentInfo(settings=self.settings) self.pnl_estimation = EstimationPanel(self, estimation=self.estimation,settings=self.settings) - self.pnl_estimation.grid(row=1, column=0, sticky="ew") + self.pnl_estimation.grid(row=1, column=0, sticky="ew",padx=5, pady=5) self.pnl_estimation.on_preview_clicked = self.pnl_settings_on_preview_clicked self.pnl_estimation.on_write_clicked = self.pnl_settings_on_write_clicked self.pnl_estimation.on_calculate_clicked = self.pnl_estimate_on_calculate_clicked diff --git a/SettingsPanel.py b/SettingsPanel.py index 9924c15..3ed405c 100644 --- a/SettingsPanel.py +++ b/SettingsPanel.py @@ -32,14 +32,14 @@ def create_widgets(self): label = ttk.Label(self, text="Settings", font=("Helvetica", 16, "bold")) - label.grid(row=0, column=0, columnspan=2, pady=10) + label.grid(row=0, column=0, columnspan=2, padx=5, pady=5) grid_frame = ttk.Frame(self) - grid_frame.grid(row=1, column=0, columnspan=2, padx=10, pady=10, sticky="nsew") + grid_frame.grid(row=1, column=0, columnspan=2, padx=5, pady=5, sticky="nsew") # Order - ttk.Label(grid_frame, text="Order:").grid(row=0, column=0, sticky=tk.E) + ttk.Label(grid_frame, text="Order:").grid(row=0, column=0, sticky=tk.E,padx=5, pady=5) self.com_order = ttk.Combobox(grid_frame, values=self.order_choices) self.com_order["values"]=self.order_choices @@ -51,7 +51,7 @@ def create_widgets(self): # Do SANS ttk.Label(grid_frame, text="Do SANS:").grid(row=1, column=0, sticky=tk.E) sans_frame = ttk.Frame(grid_frame) - sans_frame.grid(row=1, column=1, columnspan=2) + sans_frame.grid(row=1, column=1, columnspan=2,padx=5, pady=5) self.txt_times_sans = ttk.Entry(sans_frame, width=5) self.txt_times_sans.insert(0, self.settings.num_sans) # Default value self.txt_times_sans.grid(row=0, column=0, padx=5, pady=5) @@ -59,9 +59,9 @@ def create_widgets(self): self.txt_times_sans.bind("", self.update_num_sans) # Do TRANS - ttk.Label(grid_frame, text="Do TRANS:").grid(row=2, column=0, sticky=tk.E) + ttk.Label(grid_frame, text="Do TRANS:").grid(row=2, column=0, sticky=tk.E,padx=5, pady=5) trans_frame = ttk.Frame(grid_frame) - trans_frame.grid(row=2, column=1, columnspan=2) + trans_frame.grid(row=2, column=1, columnspan=2,padx=5, pady=5) self.txt_times_trans = ttk.Entry(trans_frame, width=5) self.txt_times_trans.insert(0, self.settings.num_trans) # Default value self.txt_times_trans.grid(row=0, column=0, padx=5, pady=5) @@ -71,7 +71,7 @@ def create_widgets(self): # Loop over each run self.loop_run_var = tk.BooleanVar(value=self.settings.loop_per_run) # Create BooleanVar and set default value self.chk_loop_run = ttk.Checkbutton(grid_frame, text="Loop over each run?", variable=self.loop_run_var) - self.chk_loop_run.grid(row=3, column=0, columnspan=3, pady=5) + self.chk_loop_run.grid(row=3, column=0, columnspan=3, padx=5, pady=5) self.chk_loop_run.bind("", self.update_loop_per_run) # Collection Mode @@ -83,28 +83,28 @@ def create_widgets(self): # A2 SANS - tk.Label(grid_frame, text="SANS "+self.inst_info.a2_label).grid(row=5, column=0, sticky=tk.E) + tk.Label(grid_frame, text="SANS "+self.inst_info.a2_label).grid(row=5, column=0, sticky=tk.E,padx=5, pady=5) self.com_a2_sans = ttk.Combobox(grid_frame, values=self.inst_info.a2_choices ) self.com_a2_sans.grid(row=5, column=1, columnspan=2, padx=5, pady=5) self.com_a2_sans.set(self.settings.a2_setting_sans) # Set default value self.com_a2_sans.bind("", self.update_a2_sans) # A2 TRANS - tk.Label(grid_frame, text="TRANS "+self.inst_info.a2_label).grid(row=6, column=0, sticky=tk.E) + tk.Label(grid_frame, text="TRANS "+self.inst_info.a2_label).grid(row=6, column=0, sticky=tk.E,padx=5, pady=5) self.com_a2_trans = ttk.Combobox(grid_frame, values=self.inst_info.a2_choices) self.com_a2_trans.grid(row=6, column=1, columnspan=2, padx=5, pady=5) self.com_a2_trans.set(self.settings.a2_setting_trans) # Set default value self.com_a2_trans.bind("", self.update_a2_trans) # Sample Geometry - ttk.Label(grid_frame, text="Sample Geometry:").grid(row=7, column=0, sticky=tk.E) + ttk.Label(grid_frame, text="Sample Geometry:").grid(row=7, column=0, sticky=tk.E,padx=5, pady=5) self.com_sample_geo = ttk.Combobox(grid_frame, values=self.geometry_choices) self.com_sample_geo.grid(row=7, column=1, columnspan=2, padx=5, pady=5) self.com_sample_geo.set(Geometry.get_choice_string(self.settings.geometry)) # Set default value self.com_sample_geo.bind("", self.update_sample_geometry) # Sample Height - ttk.Label(grid_frame, text="Sample Height:").grid(row=8, column=0, sticky=tk.E) + ttk.Label(grid_frame, text="Sample Height:").grid(row=8, column=0, sticky=tk.E,padx=5, pady=5) sample_height_frame = ttk.Frame(grid_frame) sample_height_frame.grid(row=8, column=1, columnspan=2) self.txt_sample_height = ttk.Entry(sample_height_frame, width=5) @@ -114,7 +114,7 @@ def create_widgets(self): self.txt_sample_height.bind("", self.update_sample_height) # Sample Width - ttk.Label(grid_frame, text="Sample Width:").grid(row=9, column=0, sticky=tk.E) + ttk.Label(grid_frame, text="Sample Width:").grid(row=9, column=0, sticky=tk.E,padx=5, pady=5) sample_width_frame = ttk.Frame(grid_frame) sample_width_frame.grid(row=9, column=1, columnspan=2) self.txt_sample_width = ttk.Entry(sample_width_frame, width=5) @@ -125,11 +125,11 @@ def create_widgets(self): # Buttons button_frame = ttk.Frame(self) - button_frame.grid(row=10, column=0, columnspan=2, pady=10) + button_frame.grid(row=10, column=0, columnspan=2,padx=5, pady=5) self.btn_preview_script = ttk.Button(button_frame, text="Preview Script", command=self.btn_preview_script_click) - self.btn_preview_script.grid(row=0, column=0, padx=5) + self.btn_preview_script.grid(row=0, column=0, padx=5, pady=5) self.btn_write_script = ttk.Button(button_frame, text="Write Script", command=self.btn_write_script_click) - self.btn_write_script.grid(row=0, column=1, padx=5) + self.btn_write_script.grid(row=0, column=1,padx=5, pady=5) # Define the Settings update methods def update_order(self, event): From 90299dd05432db8adf563ed52d75540b32f32f6a Mon Sep 17 00:00:00 2001 From: Dirk Honecker Date: Mon, 6 Jan 2025 02:14:17 +0000 Subject: [PATCH 70/88] saving and opening csv from gui --- MainWindow.py | 41 ++++++-------- TableWidget.py | 151 +++++++++---------------------------------------- 2 files changed, 44 insertions(+), 148 deletions(-) diff --git a/MainWindow.py b/MainWindow.py index 694ca40..ea37508 100644 --- a/MainWindow.py +++ b/MainWindow.py @@ -84,8 +84,8 @@ def create_widgets(self): self.btn_create_new_table = ttk.Button(button_frame, text="Create New Table", command=self.btn_create_new_table_click) self.btn_create_new_table.grid(row=0, column=0 ,padx=5, pady=5) - self.btn_open_excel = ttk.Button(button_frame, text="Open Excel", command=self.btn_open_excel_click) - self.btn_open_excel.grid(row=0, column=1,padx=5, pady=5) + self.btn_open_cvs = ttk.Button(button_frame, text="Open CSV", command=self.btn_open_csv_click) + self.btn_open_cvs.grid(row=0, column=1,padx=5, pady=5) self.mnu_main = tk.Menu(self.parent) @@ -168,38 +168,29 @@ def pnl_estimate_on_calculate_clicked(self): self.estimation.script_time = self.script_generator.estimate_run_time(self.data, self.settings.experiment, self.estimation) def btn_create_new_table_click(self): - self.data.clear() + self.data =None + self.data_frame.reset_table() - def btn_open_excel_click(self): - filepath = filedialog.askopenfilename(defaultextension=".xlsx", filetypes=[("Excel Files", "*.xls;*.xlsx")]) - if filepath: - try: - excel = ExcelIO() - excel.open_excel_workbook(filepath) - exps = excel.extract_experiments() - self.data = exps - except Exception as err: - messagebox.showerror("SANS Script - Error", "Could not open Excel spreadsheet") def mnu_exit_click(self): self.on_close() - def mnu_export_to_excel_click(self): - excel = ExcelIO() - filepath = filedialog.asksaveasfilename(defaultextension=".xls", filetypes=[("Exel File", "*.xls")]) - constructor = TableDataConstructor(TableEntries) - self.data = constructor.create_experiments(self.data_frame.data.values()) - excel.export_data_to_excel(filepath,self.runs) def mnu_save_csv_click(self): - filepath = filedialog.asksaveasfilename(defaultextension=".csv", filetypes=[("CSV File", "*.csv")]) constructor = TableDataConstructor(TableEntries) self.data = constructor.create_experiments(self.data_frame.data.values()) - if filepath: - try: - CsvExporter.export(filepath, self.data) - except Exception as err: - messagebox.showerror("SANS Script - Error", "Could not save as CSV") + + try: + self.data_frame.table.exportTable() + except Exception as err: + messagebox.showerror("SANS Script - Error", "Could not save as CSV") + + def btn_open_csv_click(self): + + try: + self.data_frame.table.importTable() + except Exception as err: + messagebox.showerror("SANS Script - Error", f"Could not open CS") def on_close(self): # Save last_instrument to settings diff --git a/TableWidget.py b/TableWidget.py index 9496e8e..66017e6 100644 --- a/TableWidget.py +++ b/TableWidget.py @@ -113,16 +113,6 @@ def show(self, callback=None): self.parentframe.columnconfigure(1,weight=1) # Configure grid layout - ''' - #self.parentframe.grid_rowconfigure(0, weight=0) - #self.parentframe.grid_rowconfigure(1, weight=1) - #self.parentframe.grid_rowconfigure(2, weight=0) - - #self.parentframe.grid_columnconfigure(0, weight=0) - #self.parentframe.grid_columnconfigure(1, weight=1) - #self.parentframe.grid_columnconfigure(2, weight=0) - #self.parentframe.grid_columnconfigure(3, weight=0) - ''' self.tablecolheader.grid(row=0,column=1,rowspan=1,sticky='news',pady=0,ipady=0) self.tablerowheader.grid(row=1,column=0,rowspan=1,sticky='news',pady=0,ipady=0) @@ -140,114 +130,7 @@ def show(self, callback=None): self.tablecolheader.xview("moveto", 0) self.xview("moveto", 0) return - ''' - def on_resize(self, event): - self.update_table_size() - - def update_table_size(self): - """Adjust the table width based on the available space in the parent frame.""" - available_width= self.parentframe.winfo_width() - if available_width <= 1.0: - available_width = self.parentframe.master.winfo_width() - print(available_width) - required_width = (self.cellwidth) * self.cols - self.tablewidth = max(available_width, required_width) - self.redrawTable() - ''' - ''' - def updateModel(self, model): - """Call this method to update the table model""" - - self.model = model - self.rows = self.model.getRowCount() - self.cols = self.model.getColumnCount() - if self.tablewidth is None: - self.tablewidth = (self.cellwidth)*self.cols - self.tablecolheader = CustomColumnHeader(self.parentframe, self) - self.tablerowheader = CustomRowHeader(self.parentframe, self) - self.createTableFrame() - return - ''' - ''' - def redrawTable(self, event=None, callback=None): - self.redrawVisible(event, callback) - return - ''' - ''' - def getVisibleRegion(self): - """Get visible region of table to display""" - - x1, y1 = self.canvasx(0), self.canvasy(0) - #w, h = self.winfo_width(), self.winfo_height() - w,h= self.parentframe.winfo_width(), self.parentframe.winfo_height() - if w <= 1.0 or h <= 1.0: - w, h = self.parentframe.master.winfo_width(), self.parentframe.master.winfo_height() - x2, y2 = self.canvasx(w), self.canvasy(h) - return x1, y1, x2, y2 - - def redrawVisible(self, event=None, callback=None): - """Redraw the visible portion of the canvas""" - - model = self.model - self.rows = self.model.getRowCount() - self.cols = self.model.getColumnCount() - if self.tablewidth is None: - self.tablewidth = (self.cellwidth) * self.cols - self.configure(bg=self.bgcolor) - self.setColPositions() - - #are we drawing a filtered subset of the recs? - if self.filtered == True and self.model.filteredrecs != None: - self.rows = len(self.model.filteredrecs) - self.delete('colrect') - - self.rowrange = range(0,self.rows) - self.configure(scrollregion=(0,0, self.tablewidth+self.x_start, - self.rowheight*self.rows+10)) - - x1, y1, x2, y2 = self.getVisibleRegion() - startvisiblerow, endvisiblerow = self.getVisibleRows(y1, y2) - self.visiblerows = range(startvisiblerow, endvisiblerow) - startvisiblecol, endvisiblecol = self.getVisibleCols(x1, x2) - self.visiblecols = range(startvisiblecol, endvisiblecol) - - if self.cols == 0 or self.rows == 0: - self.delete('entry') - self.delete('rowrect') - self.delete('currentrect') - self.delete('gridline','text') - self.tablerowheader.redraw() - return - self.drawGrid(startvisiblerow, endvisiblerow) - align = self.align - self.delete('fillrect') - for row in self.visiblerows: - if callback != None: - callback() - for col in self.visiblecols: - colname = model.getColumnName(col) - bgcolor = model.getColorAt(row,col, 'bg') - fgcolor = model.getColorAt(row,col, 'fg') - text = model.getValueAt(row,col) - self.drawText(row, col, text, fgcolor, align) - if bgcolor != None: - self.drawRect(row,col, color=bgcolor) - - #self.drawSelectedCol() - self.tablecolheader.redraw() - self.tablerowheader.redraw(align=self.align, showkeys=self.showkeynamesinheader) - #self.setSelectedRow(self.currentrow) - self.drawSelectedRow() - self.drawSelectedRect(self.currentrow, self.currentcol) - #print self.multiplerowlist - - if len(self.multiplerowlist)>1: - self.tablerowheader.drawSelectedRows(self.multiplerowlist) - self.drawMultipleRows(self.multiplerowlist) - self.drawMultipleCells() - return - ''' def deleteCells(self, rows=None, cols=None): """Clear the cell contents""" @@ -264,14 +147,32 @@ def deleteCells(self, rows=None, cols=None): self.redrawCell(row,col) return + def clearData(self, evt=None): - """Delete cells from gui event""" + """Delete all cells in the table.""" + n = messagebox.askyesno("Clear Confirm", "Clear all data?", parent=self.parentframe) + if not n: + return + + # Get all rows and columns + rows = range(self.model.getRowCount()) + cols = range(self.model.getColumnCount()) + + # Delete all cells + for col in cols: + for row in rows: + self.model.deleteCellRecord(row, col) - rows = self.multiplerowlist - cols = self.multiplecollist - self.deleteCells(rows, cols) + self.redrawTable() return + def new(self, evt=None): + """Clears all the data and makes a new table""" + constructor = TableDataConstructor(TableEntries) + EMPTY_DATA = constructor.construct_empty_data() + self.model.importDict(EMPTY_DATA) + self.redrawTable() + def fillDown(self, rowlist, collist): """Fill down a column, or multiple columns""" model = self.model @@ -381,7 +282,7 @@ def popupMenu(self, event, rows=None, cols=None, outside=None): "Delete Row(s)": lambda: self.deleteRow(), "Clear Data": lambda: self.clearData(), "Select All": self.select_All, - "New": self.new, + "New": lambda: self.new(), "Import Table": self.importTable, "Export Table": self.exportTable, "Export Reductionbatch": self.exportTable, @@ -402,6 +303,8 @@ def createSubMenu(parent, label, commands): menu.add_command(label=action, command=defaultactions[action]) return menu + + def add_commands(fieldtype): """Add commands to popup menu for column type and specific cell""" functions = self.columnactions[fieldtype] @@ -481,7 +384,9 @@ def customOption(self): print("Custom option selected") return - + def reset_table(self): + """Reset the table to its initial empty state.""" + self.table.new() From 90ae55765e0879fbe8c841d48e598ff8ef44821f Mon Sep 17 00:00:00 2001 From: Dirk Honecker Date: Mon, 6 Jan 2025 11:16:27 +0000 Subject: [PATCH 71/88] Clean reset of table when loading of new data --- MainWindow.py | 6 +++--- Scripting/ScriptGenerator.py | 8 ++++---- TableWidget.py | 28 ++++++++++++++++++++++++++-- 3 files changed, 33 insertions(+), 9 deletions(-) diff --git a/MainWindow.py b/MainWindow.py index ea37508..fc209eb 100644 --- a/MainWindow.py +++ b/MainWindow.py @@ -142,19 +142,19 @@ def load_last_instrument(self): def pnl_settings_on_write_clicked(self): - filetypes = [("genie_python Files", "*.py")] if self.settings.is_instrument in ["LARMOR", "LOQ", "ZOOM", "SANS2D"] else [("Open Genie Files", "*.gcl")] + filetypes = [("genie_python Files", "*.py")] if self.settings.experiment.is_instrument in ["LARMOR", "LOQ", "ZOOM", "SANS2D"] else [("Open Genie Files", "*.gcl")] filepath = filedialog.asksaveasfilename(defaultextension=filetypes[0][1], filetypes=filetypes) constructor = TableDataConstructor(TableEntries) self.data = constructor.create_experiments(self.data_frame.data.values()) if filepath: scriptname = filepath.split("/")[-1].replace(" ", "_").rsplit(".", 1)[0] - script_generator= ScriptGenerator(self.settings.is_instrument) + script_generator= ScriptGenerator(self.settings.experiment.is_instrument) script = script_generator.generate_script(self.data, self.settings.experiment, scriptname) with open(filepath, "w") as file: file.write(script) def pnl_settings_on_preview_clicked(self): - self.script_generator= ScriptGenerator(self.settings.is_instrument) + self.script_generator= ScriptGenerator(self.settings.experiment.is_instrument) constructor = TableDataConstructor(TableEntries) self.data = constructor.create_experiments(self.data_frame.data.values()) self.script = self.script_generator.generate_script(self.data, self.settings.experiment, "my_script") diff --git a/Scripting/ScriptGenerator.py b/Scripting/ScriptGenerator.py index 4e2649f..a30618d 100644 --- a/Scripting/ScriptGenerator.py +++ b/Scripting/ScriptGenerator.py @@ -159,13 +159,13 @@ def do_sans_run(self, script, exp, indent, settings): def aperture_string(self, size): - if size in [A2Setting.Large, A2Setting.Larmor]: + if size in [A2Setting.LARGE, A2Setting.LARMOR_LARGE]: return "large" - elif size in [A2Setting.Medium, A2Setting.Larmor]: + elif size in [A2Setting.MEDIUM, A2Setting.LARMOR_MEDIUM]: return "medium" - elif size in [A2Setting.Small, A2Setting.Larmor]: + elif size in [A2Setting.SMALL, A2Setting.LARMOR_SMALL]: return "small" - elif size in [A2Setting.XLarge, A2Setting.Larmor]: + elif size in [A2Setting.X_LARGE, A2Setting.Larmor]: return "xlarge" return "" diff --git a/TableWidget.py b/TableWidget.py index 66017e6..8ff0986 100644 --- a/TableWidget.py +++ b/TableWidget.py @@ -85,8 +85,16 @@ def ImportTableData(table, sep=None): return None model = table.getModel() + #lift focus from cell and row for clean delete + table.clearSelected() model.deleteRows() + table.data = dictdata model.importDict(dictdata) + #reset focus of selected row + table.drawCellEntry(1, table.currentcol) + table.setSelectedRow(1) + table.drawSelectedRect(1, table.currentcol) + table.drawSelectedRow() table.redrawTable() return dictdata @@ -169,9 +177,19 @@ def clearData(self, evt=None): def new(self, evt=None): """Clears all the data and makes a new table""" constructor = TableDataConstructor(TableEntries) - EMPTY_DATA = constructor.construct_empty_data() + EMPTY_DATA = constructor.construct_empty_data() + self.data = EMPTY_DATA + self.model.deleteRows() + self.model.importDict(EMPTY_DATA) + #reset focus of selected row + self.drawCellEntry(1, self.currentcol) + self.setSelectedRow(1) + self.drawSelectedRect(1, self.currentcol) + self.drawSelectedRow() self.redrawTable() + + def fillDown(self, rowlist, collist): """Fill down a column, or multiple columns""" @@ -265,7 +283,13 @@ def move_to_next_row(self, event): self.drawSelectedRow() coltype = self.model.getColumnType(self.currentcol) if coltype == "text" or coltype == "number": - self.drawCellEntry(self.currentrow, self.currentcol) + if self.currentrow < self.rows: + self.drawCellEntry(self.currentrow, self.currentcol) + else: + self.drawCellEntry(self.rows-1, self.currentcol) + self.setSelectedRow(self.rows-1) + self.drawSelectedRect(self.currentrow, self.currentcol) + self.drawSelectedRow() def popupMenu(self, event, rows=None, cols=None, outside=None): """Add left and right click behaviour for canvas, should not have to override From 257944487ec32bd4194e1cecff6b0b057fe0e106 Mon Sep 17 00:00:00 2001 From: Dirk Honecker Date: Mon, 6 Jan 2025 11:24:00 +0000 Subject: [PATCH 72/88] Setup clean new table --- TableWidget.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/TableWidget.py b/TableWidget.py index 8ff0986..2ae1219 100644 --- a/TableWidget.py +++ b/TableWidget.py @@ -178,14 +178,16 @@ def new(self, evt=None): """Clears all the data and makes a new table""" constructor = TableDataConstructor(TableEntries) EMPTY_DATA = constructor.construct_empty_data() + #clear focus of selected row + self.clearSelected() self.data = EMPTY_DATA self.model.deleteRows() self.model.importDict(EMPTY_DATA) #reset focus of selected row - self.drawCellEntry(1, self.currentcol) - self.setSelectedRow(1) - self.drawSelectedRect(1, self.currentcol) + self.drawCellEntry(0, self.currentcol) + self.setSelectedRow(0) + self.drawSelectedRect(0, self.currentcol) self.drawSelectedRow() self.redrawTable() From 44af7f857df902a3cc791b6e99a2344d29d7376d Mon Sep 17 00:00:00 2001 From: Dirk Honecker Date: Mon, 6 Jan 2025 11:42:24 +0000 Subject: [PATCH 73/88] Import export table in Gui/Menu and unify naming --- MainWindow.py | 10 ++++++---- TableDataConstructor.py | 4 ++-- TableWidget.py | 2 +- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/MainWindow.py b/MainWindow.py index fc209eb..7331325 100644 --- a/MainWindow.py +++ b/MainWindow.py @@ -80,13 +80,14 @@ def create_widgets(self): self.pnl_estimation.on_calculate_clicked = self.pnl_estimate_on_calculate_clicked # Buttons button_frame = ttk.Frame(self) - button_frame.grid(row=2, column=0, sticky="w",padx=5, pady=5) + button_frame.grid(row=3, column=0, sticky="w",padx=5, pady=5) self.btn_create_new_table = ttk.Button(button_frame, text="Create New Table", command=self.btn_create_new_table_click) self.btn_create_new_table.grid(row=0, column=0 ,padx=5, pady=5) - self.btn_open_cvs = ttk.Button(button_frame, text="Open CSV", command=self.btn_open_csv_click) + self.btn_open_cvs = ttk.Button(button_frame, text="Import Table", command=self.btn_open_csv_click) self.btn_open_cvs.grid(row=0, column=1,padx=5, pady=5) - + self.btn_save_csv = ttk.Button(button_frame, text="Export Table", command=self.mnu_save_csv_click) + self.btn_save_csv.grid(row=0, column=2,padx=5, pady=5) self.mnu_main = tk.Menu(self.parent) self.parent.config(menu=self.mnu_main) @@ -94,7 +95,8 @@ def create_widgets(self): file_menu = tk.Menu(self.mnu_main, tearoff=0) self.mnu_main.add_cascade(label="File", menu=file_menu) #file_menu.add_command(label="Export to Excel", command=self.mnu_export_to_excel_click) - file_menu.add_command(label="Save as CSV", command=self.mnu_save_csv_click) + file_menu.add_command(label="Import Table", command=self.btn_open_csv_click) + file_menu.add_command(label="Export Table", command=self.mnu_save_csv_click) file_menu.add_separator() file_menu.add_command(label="Exit", command=self.mnu_exit_click) diff --git a/TableDataConstructor.py b/TableDataConstructor.py index d6bc8b0..db93395 100644 --- a/TableDataConstructor.py +++ b/TableDataConstructor.py @@ -7,14 +7,14 @@ def __init__(self, table_entries): def construct_empty_data(self): empty_data = { - "rec1": {entry: "" for entry in self.table_entries} + 0: {entry: "" for entry in self.table_entries} } return empty_data def construct_data(self, data_list): data = {} for i, row in enumerate(data_list, start=1): - data[f"rec{i}"] = {entry: value for entry, value in zip(self.table_entries, row)} + data[i] = {entry: value for entry, value in zip(self.table_entries, row)} return data def deconstruct_data(self, data_dict): diff --git a/TableWidget.py b/TableWidget.py index 2ae1219..253837b 100644 --- a/TableWidget.py +++ b/TableWidget.py @@ -308,7 +308,7 @@ def popupMenu(self, event, rows=None, cols=None, outside=None): "Delete Row(s)": lambda: self.deleteRow(), "Clear Data": lambda: self.clearData(), "Select All": self.select_All, - "New": lambda: self.new(), + "Create New Table": lambda: self.new(), "Import Table": self.importTable, "Export Table": self.exportTable, "Export Reductionbatch": self.exportTable, From b0d28942ffd88a93d4001475d38acde81fd24ec2 Mon Sep 17 00:00:00 2001 From: Dirk Honecker Date: Mon, 6 Jan 2025 15:29:03 +0000 Subject: [PATCH 74/88] Pass loaded table on to scripts by setting up data at the right places --- TableWidget.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/TableWidget.py b/TableWidget.py index 253837b..20898ee 100644 --- a/TableWidget.py +++ b/TableWidget.py @@ -90,6 +90,8 @@ def ImportTableData(table, sep=None): model.deleteRows() table.data = dictdata model.importDict(dictdata) + # Update the data in the TableWidget instance + table.parentframe.data = dictdata #reset focus of selected row table.drawCellEntry(1, table.currentcol) table.setSelectedRow(1) @@ -215,8 +217,8 @@ def display_sheet_data(self,name=None): sheet = self.workbook[sheet_name] #TableEntries = "col1, col2, label, col3, col4, label2, col5, col6, label3" constructor = TableDataConstructor(TableEntries) - data = constructor.construct_data(sheet.iter_rows(values_only=True)) - self.data = data + self.data = constructor.construct_data(sheet.iter_rows(values_only=True)) + self.model.importDict(data) self.redraw() @@ -389,7 +391,7 @@ def __init__(self, parent=None, data=None): if data is None: data = TableWidget.EMPTY_DATA self.data = data - self.table = CustomTableCanvas(self, data=self.data) + self.table = CustomTableCanvas(self, data=self.data ) #table.importCSV("test.csv") print (self.table.model.columnNames) #table.model.data[1]["a"] = "XX" From f6237698aa32de3f7c575c0228ff8eb11c4e9516 Mon Sep 17 00:00:00 2001 From: Dirk Honecker Date: Mon, 6 Jan 2025 18:06:58 +0000 Subject: [PATCH 75/88] Rescale Preview window when expanding and propagate settings to script --- Dialogs/PreviewDialog.py | 10 ++++- MainWindow.py | 26 +++++------ Models/A2Setting.py | 16 +++---- Models/ExperimentSettings.py | 24 +++++----- Models/Geometry.py | 8 ++-- Models/InstrumentInfo.py | 20 +++++---- Properties/Settings.py | 6 +-- Scripting/ScriptGenerator.py | 87 +++++++++++++++++++----------------- SettingsPanel.py | 4 +- 9 files changed, 106 insertions(+), 95 deletions(-) diff --git a/Dialogs/PreviewDialog.py b/Dialogs/PreviewDialog.py index f822d55..5cf56cc 100644 --- a/Dialogs/PreviewDialog.py +++ b/Dialogs/PreviewDialog.py @@ -9,10 +9,14 @@ def __init__(self, parent, script=None): self.parent = parent # Store reference to the parent (main window) self.title("PreviewDialog") - self.geometry("600x500") + #self.geometry("600x500") + self.resizable(True, True) + self.columnconfigure(0,weight=1) + self.rowconfigure(0,weight=1) self.grid_rowconfigure(0, weight=1) self.grid_columnconfigure(0, weight=1) + self.rowconfigure(0, weight=1) # Create a frame for the text area with a border border_frame = tk.Frame(self, borderwidth=1, relief="solid") @@ -20,7 +24,9 @@ def __init__(self, parent, script=None): # Create a scrollable text area self.txt_script = scrolledtext.ScrolledText(border_frame, wrap=tk.WORD) - self.txt_script.grid(row=0, column=0, sticky="nsew",padx=5, pady=5) + self.txt_script.grid(row=0, column=0, sticky="nsew", padx=5, pady=5) + border_frame.grid_rowconfigure(0, weight=1) + border_frame.grid_columnconfigure(0, weight=1) self.txt_script.insert(tk.END, script) self.txt_script.config(state=tk.DISABLED) diff --git a/MainWindow.py b/MainWindow.py index 7331325..397f66d 100644 --- a/MainWindow.py +++ b/MainWindow.py @@ -29,8 +29,8 @@ def __init__(self,parent): self.settings = Settings() self.estimation = Estimation(settings=self.settings) - self.settings.experiment = ExperimentSettings(settings=self.settings, estimation=self.estimation) - self.inst_info = InstrumentInfo(settings=self.settings) + self.experiment = ExperimentSettings(settings=self.settings, estimation=self.estimation) + self.inst_info = InstrumentInfo(settings=self.settings,experiment=self.experiment) self.create_widgets() self.load_last_instrument() @@ -103,10 +103,10 @@ def create_widgets(self): instrument_menu = tk.Menu(self.mnu_main, tearoff=0) self.mnu_main.add_cascade(label="Instrument", menu=instrument_menu) - instrument_menu.add_radiobutton(label="LARMOR", variable=self.settings.experiment.instrument, value="LARMOR", command=self.update_panel) - instrument_menu.add_radiobutton(label="LOQ", variable=self.settings.experiment.instrument, value="LOQ", command=self.update_panel) - instrument_menu.add_radiobutton(label="SANS2D", variable=self.settings.experiment.instrument, value="SANS2D", command=self.update_panel) - instrument_menu.add_radiobutton(label="ZOOM", variable=self.settings.experiment.instrument, value="ZOOM", command=self.update_panel) + instrument_menu.add_radiobutton(label="LARMOR", variable=self.experiment.instrument, value="LARMOR", command=self.update_panel) + instrument_menu.add_radiobutton(label="LOQ", variable=self.experiment.instrument, value="LOQ", command=self.update_panel) + instrument_menu.add_radiobutton(label="SANS2D", variable=self.experiment.instrument, value="SANS2D", command=self.update_panel) + instrument_menu.add_radiobutton(label="ZOOM", variable=self.experiment.instrument, value="ZOOM", command=self.update_panel) """ @@ -138,28 +138,28 @@ def update_panel(self): def load_last_instrument(self): inst = self.settings.last_instrument # Replace with actual settings retrieval - self.settings.experiment.is_instrument = inst + self.experiment.is_instrument = inst def pnl_settings_on_write_clicked(self): - filetypes = [("genie_python Files", "*.py")] if self.settings.experiment.is_instrument in ["LARMOR", "LOQ", "ZOOM", "SANS2D"] else [("Open Genie Files", "*.gcl")] + filetypes = [("genie_python Files", "*.py")] if self.experiment.is_instrument in ["LARMOR", "LOQ", "ZOOM", "SANS2D"] else [("Open Genie Files", "*.gcl")] filepath = filedialog.asksaveasfilename(defaultextension=filetypes[0][1], filetypes=filetypes) constructor = TableDataConstructor(TableEntries) self.data = constructor.create_experiments(self.data_frame.data.values()) if filepath: scriptname = filepath.split("/")[-1].replace(" ", "_").rsplit(".", 1)[0] - script_generator= ScriptGenerator(self.settings.experiment.is_instrument) - script = script_generator.generate_script(self.data, self.settings.experiment, scriptname) + script_generator= ScriptGenerator(self.settings) + script = script_generator.generate_script(self.data, scriptname) with open(filepath, "w") as file: file.write(script) def pnl_settings_on_preview_clicked(self): - self.script_generator= ScriptGenerator(self.settings.experiment.is_instrument) + self.script_generator= ScriptGenerator(self.settings) constructor = TableDataConstructor(TableEntries) self.data = constructor.create_experiments(self.data_frame.data.values()) - self.script = self.script_generator.generate_script(self.data, self.settings.experiment, "my_script") + self.script = self.script_generator.generate_script(self.data, "my_script") print(self.script) preview_dialog = PreviewDialog(self, self.script) preview_dialog.show() @@ -167,7 +167,7 @@ def pnl_settings_on_preview_clicked(self): def pnl_estimate_on_calculate_clicked(self): constructor = TableDataConstructor(TableEntries) self.data = constructor.create_experiments(self.data_frame.data.values()) - self.estimation.script_time = self.script_generator.estimate_run_time(self.data, self.settings.experiment, self.estimation) + self.estimation.script_time = self.script_generator.estimate_run_time(self.data, self.estimation) def btn_create_new_table_click(self): self.data =None diff --git a/Models/A2Setting.py b/Models/A2Setting.py index 9533cac..740b756 100644 --- a/Models/A2Setting.py +++ b/Models/A2Setting.py @@ -1,12 +1,12 @@ class A2Setting: - X_LARGE = "XLarge = 20x18 mm" - LARGE = "Large = 17 mm" - MEDIUM = "Medium = 12 mm" - SMALL = "Small = 5/1 mm" - TRANS = "Trans = 1 mm" - LARMOR_LARGE = "Large = 25, 16 mm" - LARMOR_MEDIUM = "Medium = 20, 14 mm" - LARMOR_SMALL = "Small = 15, 10 mm" + X_LARGE = "XLarge (20x18 mm)" + LARGE = "Large (17 mm)" + MEDIUM = "Medium (12 mm)" + SMALL = "Small (5/1 mm)" + TRANS = "Trans (1 mm)" + LARMOR_LARGE = "Large (25, 16 mm)" + LARMOR_MEDIUM = "Medium (20, 14 mm)" + LARMOR_SMALL = "Small (15, 10 mm)" LARMOR_choices = [ diff --git a/Models/ExperimentSettings.py b/Models/ExperimentSettings.py index 1b08735..490a3af 100644 --- a/Models/ExperimentSettings.py +++ b/Models/ExperimentSettings.py @@ -2,7 +2,6 @@ from Properties.Settings import Settings from Models.A2Setting import A2Setting from Models.DataCollection import DataCollection -from Models.InstrumentInfo import InstrumentInfo from Models.Estimation import Estimation from Properties.Settings import Settings from Models.RunOrder import RunOrder @@ -11,16 +10,17 @@ class ExperimentSettings: def __init__(self, settings=None, estimation=None): self.settings = settings if settings is not None else Settings() - self.estimation = estimation if estimation is not None else Estimation(settings=self.settings) + self.estimation = estimation if estimation is not None else Estimation(settings=self.settings) + - self._order = tk.StringVar(value=RunOrder.get_choice_string(self.settings.run_order)) + self._run_order = tk.StringVar(value=RunOrder.get_choice_string(self.settings.run_order)) self._num_sans = tk.IntVar(value=self.settings.num_sans) self._num_trans = tk.IntVar(value=self.settings.num_trans) self._loop_per_run = tk.BooleanVar(value=self.settings.loop_per_run) self._a2_setting_sans = tk.StringVar(value=self.settings.a2_setting_sans) self._a2_setting_trans = tk.StringVar(value=self.settings.a2_setting_trans) - self._sample_geometry = tk.StringVar(value=Geometry.get_choice_string(self.settings.geometry)) + self._sample_geometry = tk.StringVar(value=Geometry.get_choice_string(self.settings.sample_geometry)) self._sample_height = tk.DoubleVar(value=self.settings.sample_height) self._sample_width = tk.DoubleVar(value=self.settings.sample_width) self._sample_width_enabled = tk.BooleanVar(value=True) @@ -33,7 +33,7 @@ def __init__(self, settings=None, estimation=None): self.instrument.trace_add("write", self.on_property_changed) self.instrument.trace_add("write", self.update_instrument) - self._order.trace_add("write", self.on_property_changed) + self._run_order.trace_add("write", self.on_property_changed) self._num_sans.trace_add("write", self.on_property_changed) self._num_trans.trace_add("write", self.on_property_changed) self._loop_per_run.trace_add("write", self.on_property_changed) @@ -51,7 +51,7 @@ def __init__(self, settings=None, estimation=None): self._loop_per_run.trace_add("write", self.validate) - self._order.trace_add("write", self.save_changes) + self._run_order.trace_add("write", self.save_changes) self._num_sans.trace_add("write", self.save_changes) self._num_trans.trace_add("write", self.save_changes) self._loop_per_run.trace_add("write", self.save_changes) @@ -142,25 +142,25 @@ def set_instrument(self, instrument, a2_label, a2_choices, data_collection_label def save_changes(self, *args): - self.settings.run_order = self.order + self.settings.run_order = self.run_order self.settings.num_sans = self.num_sans self.settings.num_trans = self.num_trans self.settings.loop_per_run = self.loop_per_run self.settings.a2_setting_sans = self.a2_setting_sans self.settings.a2_setting_trans = self.a2_setting_trans - self.settings.geometry = self.sample_geometry + self.settings.sample_geometry = self.sample_geometry self.settings.sample_height = self.sample_height self.settings.sample_width = self.sample_width self.settings.collection_mode = self.collection_mode @property - def order(self): + def run_order(self): return self._order.get() - @order.setter - def order(self, value): - self._order.set(value) + @run_order.setter + def run_order(self, value): + self._run_order.set(value) @property def num_sans(self): diff --git a/Models/Geometry.py b/Models/Geometry.py index b8c2269..966004a 100644 --- a/Models/Geometry.py +++ b/Models/Geometry.py @@ -1,8 +1,8 @@ class Geometry: - DISC = [1, "1: Disc"] - CYLINDRICAL = [2, "2: Cylindrical"] - FLAT_PLATE = [3, "3: Flat Plate"] - SINGLE_CRYSTAL = [4, "4: Single Crystal"] + DISC = [1, "Disc"] + CYLINDRICAL = [2, "Cylindrical"] + FLAT_PLATE = [3, "Flat Plate"] + SINGLE_CRYSTAL = [4, "Single Crystal"] choices = [ diff --git a/Models/InstrumentInfo.py b/Models/InstrumentInfo.py index 896ba05..6ad59ee 100644 --- a/Models/InstrumentInfo.py +++ b/Models/InstrumentInfo.py @@ -1,7 +1,8 @@ import tkinter as tk from tkinter import StringVar, BooleanVar from typing import List -from Properties.Settings import Settings +#from Properties.Settings import Settings +from Models.ExperimentSettings import ExperimentSettings class InstrumentInfo: property_changed = [] @@ -14,14 +15,15 @@ class InstrumentInfo: a2_trans_enabled = False collection_mode_enabled = True - def __init__(self,settings=None): - self.settings = settings if settings is not None else Settings() - self._a2_label = StringVar(value=self.settings.experiment.a2_label) - self._a2_choices = tk.Variable(value=self.settings.experiment.a2_choices) - self._data_collection_label = StringVar(value=self.settings.experiment.data_collection_label) - self._data_collection_choices = tk.Variable(value=self.settings.experiment.data_collection_choices) - self._a2_trans_enabled = BooleanVar(value=self.settings.experiment.a2_trans_enabled) - self._collection_mode_enabled = BooleanVar(value=self.settings.experiment.collection_mode_enabled) + def __init__(self,settings=None, experiment=None): + #self.settings = settings if settings is not None else Settings() + self.experiment = experiment if experiment is not None else ExperimentSettings() + self._a2_label = StringVar(value=self.experiment.a2_label) + self._a2_choices = tk.Variable(value=self.experiment.a2_choices) + self._data_collection_label = StringVar(value=self.experiment.data_collection_label) + self._data_collection_choices = tk.Variable(value=self.experiment.data_collection_choices) + self._a2_trans_enabled = BooleanVar(value=self.experiment.a2_trans_enabled) + self._collection_mode_enabled = BooleanVar(value=self.experiment.collection_mode_enabled) self._script_generator = None diff --git a/Properties/Settings.py b/Properties/Settings.py index 1f80c4d..5bda93c 100644 --- a/Properties/Settings.py +++ b/Properties/Settings.py @@ -38,11 +38,11 @@ def sample_width(self, value): self.save() @property - def geometry(self): + def sample_geometry(self): return self.dict_settings.get("DEFAULT","Geometry") - @geometry.setter - def geometry(self, value): + @sample_geometry.setter + def sample_geometry(self, value): self.dict_settings.set("DEFAULT","Geometry", str(value)) self.save() diff --git a/Scripting/ScriptGenerator.py b/Scripting/ScriptGenerator.py index a30618d..aa69856 100644 --- a/Scripting/ScriptGenerator.py +++ b/Scripting/ScriptGenerator.py @@ -3,6 +3,7 @@ from datetime import datetime, timedelta from Models.Estimation import Estimation from Models.Experiment import Experiment +from Properties.Settings import Settings from Models.ExperimentSettings import ExperimentSettings from Models.A2Setting import A2Setting from Models.RunOrder import RunOrder @@ -10,16 +11,18 @@ class ScriptGenerator: - def __init__(self, instrument=None): + def __init__(self, settings=None): + self.settings = settings if settings is not None else Settings() + #self.experiment = experiment if experiment is not None else ExperimentSettings() self.indent = " " - self.instrument = instrument + self.instrument = self.settings.last_instrument - def generate_script(self, runs, settings, scriptname): + def generate_script(self, runs, scriptname): script = f"# Script created by SANSScript at {datetime.now()}\n\n" script += self.generate_header(scriptname) - script += self.generate_setup(settings) - script += self.generate_runs(runs, settings) + script += self.generate_setup() + script += self.generate_runs(runs) script += "\n" return script @@ -43,11 +46,11 @@ def generate_header(self, scriptname): script += f"\ndef {scriptname}():\n" return script - def generate_setup(self, settings): + def generate_setup(self): script = "" - script += self.get_indent_string(1) + f"g.change_sample_par('Width', '{settings.sample_width}')\n" - script += self.get_indent_string(1) + f"g.change_sample_par('height', '{settings.sample_height}')\n" - script += self.get_indent_string(1) + f"g.change_sample_par('Geometry', '{settings.sample_geometry}')\n\n" + script += self.get_indent_string(1) + f"g.change_sample_par('Width', '{self.settings.sample_width}')\n" + script += self.get_indent_string(1) + f"g.change_sample_par('height', '{self.settings.sample_height}')\n" + script += self.get_indent_string(1) + f"g.change_sample_par('Geometry', '{self.settings.sample_geometry}')\n\n" return script def check_positions(self, runs): @@ -60,61 +63,61 @@ def check_positions(self, runs): script += self.get_indent_string(2) + "return\n\n" return script - def generate_runs(self, runs, settings): + def generate_runs(self, runs): script = self.check_positions(runs) if not runs: return script - if float(settings.settings.run_order) == RunOrder.ALL_TRANS[0]: - script += self.get_indent_string(1) + f"for i in range({settings.num_trans}):\n" + if float(self.settings.run_order) == RunOrder.ALL_TRANS[0]: + script += self.get_indent_string(1) + f"for i in range({self.settings.num_trans}):\n" for exp in runs: if exp.trans: - script = self.do_trans_run(script, exp, 2, settings) - script += self.get_indent_string(1) + f"for i in range({settings.num_sans}):\n" + script = self.do_trans_run(script, exp, 2) + script += self.get_indent_string(1) + f"for i in range({self.settings.num_sans}):\n" for exp in runs: if exp.sans: - script = self.do_sans_run(script, exp, 2, settings) - elif float(settings.settings.run_order) == RunOrder.ALL_SANS[0]: - script += self.get_indent_string(1) + f"for i in range({settings.num_sans}):\n" + script = self.do_sans_run(script, exp, 2) + elif float(self.settings.run_order) == RunOrder.ALL_SANS[0]: + script += self.get_indent_string(1) + f"for i in range({self.settings.num_sans}):\n" for exp in runs: if exp.sans: - script = self.do_sans_run(script, exp, 2, settings) - if settings.num_trans and any(exp.trans for exp in runs): - script += self.get_indent_string(1) + f"for i in range({settings.num_trans}):\n" + script = self.do_sans_run(script, exp, 2) + if self.settings.num_trans and any(exp.trans for exp in runs): + script += self.get_indent_string(1) + f"for i in range({self.settings.num_trans}):\n" for exp in runs: if exp.trans: - script = self.do_trans_run(script, exp, 2, settings) + script = self.do_trans_run(script, exp, 2) else: - script += self.get_indent_string(1) + f"num_sans = {settings.num_sans}\n" - script += self.get_indent_string(1) + f"num_trans = {settings.num_trans}\n" + script += self.get_indent_string(1) + f"num_sans = {self.settings.num_sans}\n" + script += self.get_indent_string(1) + f"num_trans = {self.settings.num_trans}\n" script += self.get_indent_string(1) + "count = 0\n" script += self.get_indent_string(1) + "while True:\n" indent = 2 for exp in runs: - if float(settings.settings.run_order) == RunOrder.TRANS_FIRST[0]: + if float(self.settings.run_order) == RunOrder.TRANS_FIRST[0]: if exp.trans: script += self.get_indent_string(indent) + "if count < num_trans:\n" - script = self.do_trans_run(script, exp, indent + 1, settings) + script = self.do_trans_run(script, exp, indent + 1) if exp.sans: script += self.get_indent_string(indent) + "if count < num_sans:\n" - script = self.do_sans_run(script, exp, indent + 1, settings) + script = self.do_sans_run(script, exp, indent + 1) else: if exp.sans: script += self.get_indent_string(indent) + "if count < num_sans:\n" - script = self.do_sans_run(script, exp, indent + 1, settings) + script = self.do_sans_run(script, exp, indent + 1) if exp.trans: script += self.get_indent_string(indent) + "if count < num_trans:\n" - script = self.do_trans_run(script, exp, indent + 1, settings) + script = self.do_trans_run(script, exp, indent + 1) script += self.get_indent_string(indent) + "count += 1\n" script += self.get_indent_string(indent) + "if count >= num_trans and count >= num_sans: break\n" return script - def do_trans_run(self, script, exp, indent, settings): + def do_trans_run(self, script, exp, indent): script += self.do_sample_environment(exp, indent) waitfor = "uamps" rtype = "histogram" fulltitle = f"{{{exp.sample}}}" - if settings.collection_mode == DataCollection.EVENTS: + if self.settings.collection_mode == DataCollection.EVENTS: rtype = DataCollection.EVENTS if exp.trans_wait == "Frames": waitfor = "frames" @@ -127,22 +130,22 @@ def do_trans_run(self, script, exp, indent, settings): elif self.instrument == "LOQ": script += self.get_indent_string(indent) + f"do_trans(title='{fulltitle}', pos='{exp.position}', {waitfor}={exp.trans}, thickness={exp.thickness}, aperture='small')\n" else: - script += self.get_indent_string(indent) + f"do_trans(title='{fulltitle}', pos='{exp.position}', {waitfor}={exp.trans}, thickness={exp.thickness}, dae='transmission', aperture='{self.aperture_string(settings.a2_setting_trans)}')\n" + script += self.get_indent_string(indent) + f"do_trans(title='{fulltitle}', pos='{exp.position}', {waitfor}={exp.trans}, thickness={exp.thickness}, dae='transmission', aperture='{self.aperture_string(self.settings.a2_setting_trans)}')\n" script += self.get_indent_string(indent) + exp.post_command + "\n" return script - def do_sans_run(self, script, exp, indent, settings): + def do_sans_run(self, script, exp, indent): script += self.do_sample_environment(exp, indent) waitfor = "uamps" rtype = "histogram" fulltitle = f"{{{exp.sample}}}" if exp.background: fulltitle += f"_{{{exp.background}}}" - if settings.collection_mode in ["Events", "TRANSSANS_Events"]: + if self.settings.collection_mode in ["Events", "TRANSSANS_Events"]: rtype = "event" - elif settings.collection_mode == "SESANS": + elif self.settings.collection_mode == "SESANS": rtype = "sesans" - if settings.collection_mode in ["TRANSSANS_Events", "TRANSSANS_Histogram"]: + if self.settings.collection_mode in ["TRANSSANS_Events", "TRANSSANS_Histogram"]: fulltitle += "_TRANS" if exp.sans_wait == "Frames": waitfor = "frames" @@ -153,7 +156,7 @@ def do_sans_run(self, script, exp, indent, settings): if self.instrument == "LARMOR" or self.instrument == "ZOOM": script += self.get_indent_string(indent) + f"do_sans(title='{fulltitle}', pos='{exp.position}', {waitfor}={exp.sans}, thickness={exp.thickness}, dae='{rtype}')\n" else: - script += self.get_indent_string(indent) + f"do_sans(title='{fulltitle}', pos='{exp.position}', {waitfor}={exp.sans}, thickness={exp.thickness}, dae='{rtype}', aperture='{self.aperture_string(settings.a2_setting_sans)}')\n" + script += self.get_indent_string(indent) + f"do_sans(title='{fulltitle}', pos='{exp.position}', {waitfor}={exp.sans}, thickness={exp.thickness}, dae='{rtype}', aperture='{self.aperture_string(self.settings.a2_setting_sans)}')\n" script += self.get_indent_string(indent) + exp.post_command + "\n" return script @@ -165,7 +168,7 @@ def aperture_string(self, size): return "medium" elif size in [A2Setting.SMALL, A2Setting.LARMOR_SMALL]: return "small" - elif size in [A2Setting.X_LARGE, A2Setting.Larmor]: + elif size in [A2Setting.X_LARGE]: return "xlarge" return "" @@ -205,12 +208,12 @@ def do_sample_environment(self, exp, indent): script += self.get_indent_string(indent) + f"g.cset(SHEAR_ANGLE_2={exp.shear_angle2})\n" return script - def estimate_run_time(self, runs, settings, estimation): + def estimate_run_time(self, runs, estimation): minutes = 0 - if settings.num_sans > 0: - minutes += self.calculate_time(runs, True, estimation.count_rate, estimation.move_time) * settings.num_sans - if settings.num_trans > 0: - minutes += self.calculate_time(runs, False, estimation.count_rate, estimation.move_time) * settings.num_trans + if self.settings.num_sans > 0: + minutes += self.calculate_time(runs, True, estimation.count_rate, estimation.move_time) * self.settings.num_sans + if self.settings.num_trans > 0: + minutes += self.calculate_time(runs, False, estimation.count_rate, estimation.move_time) * self.settings.num_trans time = datetime(1, 1, 1, 0, 0, 0) + timedelta(minutes=minutes) return time.strftime("%H:%M:%S") diff --git a/SettingsPanel.py b/SettingsPanel.py index 3ed405c..b75eb81 100644 --- a/SettingsPanel.py +++ b/SettingsPanel.py @@ -100,7 +100,7 @@ def create_widgets(self): ttk.Label(grid_frame, text="Sample Geometry:").grid(row=7, column=0, sticky=tk.E,padx=5, pady=5) self.com_sample_geo = ttk.Combobox(grid_frame, values=self.geometry_choices) self.com_sample_geo.grid(row=7, column=1, columnspan=2, padx=5, pady=5) - self.com_sample_geo.set(Geometry.get_choice_string(self.settings.geometry)) # Set default value + self.com_sample_geo.set(Geometry.get_choice_string(self.settings.sample_geometry)) # Set default value self.com_sample_geo.bind("", self.update_sample_geometry) # Sample Height @@ -157,7 +157,7 @@ def update_a2_trans(self, event): self.settings.a2_setting_trans = self.com_a2_trans.get() def update_sample_geometry(self, event): - self.settings.geometry = Geometry.get_ordinal_from_string(self.com_sample_geo.get()) + self.settings.sample_geometry = Geometry.get_ordinal_from_string(self.com_sample_geo.get()) def update_sample_height(self, event): self.settings.sample_height = self.txt_sample_height.get() From 136b1a4d94adab69c342889e3dc7bd9799e84466 Mon Sep 17 00:00:00 2001 From: Dirk Honecker Date: Tue, 7 Jan 2025 14:38:40 +0000 Subject: [PATCH 76/88] Load in data from table into script --- MainWindow.py | 25 +++++++++++++++---------- TableDataConstructor.py | 13 ++++++++----- TableWidget.py | 12 +++--------- 3 files changed, 26 insertions(+), 24 deletions(-) diff --git a/MainWindow.py b/MainWindow.py index 397f66d..ec655ef 100644 --- a/MainWindow.py +++ b/MainWindow.py @@ -146,28 +146,35 @@ def load_last_instrument(self): def pnl_settings_on_write_clicked(self): filetypes = [("genie_python Files", "*.py")] if self.experiment.is_instrument in ["LARMOR", "LOQ", "ZOOM", "SANS2D"] else [("Open Genie Files", "*.gcl")] filepath = filedialog.asksaveasfilename(defaultextension=filetypes[0][1], filetypes=filetypes) - constructor = TableDataConstructor(TableEntries) - self.data = constructor.create_experiments(self.data_frame.data.values()) + constructor = TableDataConstructor(TableEntries) + + + self.data = constructor.construct_data(self.data_frame.table.model.getAllCells()) + self.experiment_data=constructor.create_experiments(self.data.values()) + if filepath: scriptname = filepath.split("/")[-1].replace(" ", "_").rsplit(".", 1)[0] script_generator= ScriptGenerator(self.settings) - script = script_generator.generate_script(self.data, scriptname) + script = script_generator.generate_script(self.experiment_data, scriptname) with open(filepath, "w") as file: file.write(script) def pnl_settings_on_preview_clicked(self): self.script_generator= ScriptGenerator(self.settings) - constructor = TableDataConstructor(TableEntries) - self.data = constructor.create_experiments(self.data_frame.data.values()) - self.script = self.script_generator.generate_script(self.data, "my_script") + constructor = TableDataConstructor(TableEntries) + + self.data = constructor.construct_data(self.data_frame.table.model.getAllCells()) + self.experiment_data=constructor.create_experiments(self.data.values()) + self.script = self.script_generator.generate_script(self.experiment_data, "my_script") print(self.script) preview_dialog = PreviewDialog(self, self.script) preview_dialog.show() def pnl_estimate_on_calculate_clicked(self): constructor = TableDataConstructor(TableEntries) - self.data = constructor.create_experiments(self.data_frame.data.values()) - self.estimation.script_time = self.script_generator.estimate_run_time(self.data, self.estimation) + self.data = constructor.construct_data(self.data_frame.table.model.getAllCells()) + self.experiment_data=constructor.create_experiments(self.data.values()) + self.estimation.script_time = self.script_generator.estimate_run_time(self.experiment_data, self.estimation) def btn_create_new_table_click(self): self.data =None @@ -179,8 +186,6 @@ def mnu_exit_click(self): def mnu_save_csv_click(self): - constructor = TableDataConstructor(TableEntries) - self.data = constructor.create_experiments(self.data_frame.data.values()) try: self.data_frame.table.exportTable() diff --git a/TableDataConstructor.py b/TableDataConstructor.py index db93395..4ff5b58 100644 --- a/TableDataConstructor.py +++ b/TableDataConstructor.py @@ -7,19 +7,19 @@ def __init__(self, table_entries): def construct_empty_data(self): empty_data = { - 0: {entry: "" for entry in self.table_entries} + 1: {entry: "" for entry in self.table_entries} } return empty_data def construct_data(self, data_list): data = {} - for i, row in enumerate(data_list, start=1): + for i, row in enumerate(data_list.values(), start=1): data[i] = {entry: value for entry, value in zip(self.table_entries, row)} return data def deconstruct_data(self, data_dict): data_list = [] - for key in sorted(data_dict.keys(), key=lambda x: int(x[3:])): + for key in sorted(data_dict.keys()): row = [data_dict[key][entry] for entry in self.table_entries] data_list.append(row) return data_list @@ -58,7 +58,7 @@ def create_experiments(self, data_list): constructor = TableDataConstructor(TableEntries) empty_data = constructor.construct_empty_data() print(empty_data) - + ''' # Example usage TableEntries = "col1, col2, label, col3, col4, label2, col5, col6, label3" constructor = TableDataConstructor(TableEntries) @@ -89,8 +89,11 @@ def create_experiments(self, data_list): data = constructor.construct_data(data_list) print("Data:", data) - + ''' # Create experiments from the constructed data + data_list= {0: ['AB', '10', '', '10', '', '', '', '', '1', '', '', '', '', '', '', '', '', '', ''], 1: ['CD', '3', '', '2', '', '', '', '', '2', '', '', '', '', '', '', '', '', '', ''], 2: ['DF', '2', '', '4', '', '', '', '', '5', '', '', '', '', '', '', '', '', '', '']} + data = constructor.construct_data(data_list) + print("Data:", data) experiments = constructor.create_experiments(data.values()) for exp in experiments: print(exp.position, exp.trans, exp.sans, exp.temperature1, exp.rb_number) diff --git a/TableWidget.py b/TableWidget.py index 20898ee..784a117 100644 --- a/TableWidget.py +++ b/TableWidget.py @@ -88,10 +88,7 @@ def ImportTableData(table, sep=None): #lift focus from cell and row for clean delete table.clearSelected() model.deleteRows() - table.data = dictdata model.importDict(dictdata) - # Update the data in the TableWidget instance - table.parentframe.data = dictdata #reset focus of selected row table.drawCellEntry(1, table.currentcol) table.setSelectedRow(1) @@ -182,7 +179,6 @@ def new(self, evt=None): EMPTY_DATA = constructor.construct_empty_data() #clear focus of selected row self.clearSelected() - self.data = EMPTY_DATA self.model.deleteRows() self.model.importDict(EMPTY_DATA) @@ -215,9 +211,7 @@ def display_sheet_data(self,name=None): sheet_name = name if sheet_name and self.workbook: sheet = self.workbook[sheet_name] - #TableEntries = "col1, col2, label, col3, col4, label2, col5, col6, label3" - constructor = TableDataConstructor(TableEntries) - self.data = constructor.construct_data(sheet.iter_rows(values_only=True)) + #TableEntries = "col1, col2, label, col3, col4, label2, col5, col6, label3" self.model.importDict(data) self.redraw() @@ -390,8 +384,8 @@ def __init__(self, parent=None, data=None): self.parent = parent if data is None: data = TableWidget.EMPTY_DATA - self.data = data - self.table = CustomTableCanvas(self, data=self.data ) + + self.table = CustomTableCanvas(self, data=data ) #table.importCSV("test.csv") print (self.table.model.columnNames) #table.model.data[1]["a"] = "XX" From 500a63e0f36b0240359132955cb57f73dfba293f Mon Sep 17 00:00:00 2001 From: Dirk Honecker Date: Tue, 7 Jan 2025 16:12:09 +0000 Subject: [PATCH 77/88] Create DropdownBox when SANS/TRANS wait is pressed --- Models/ExperimentSettings.py | 26 +++++------ Properties/Settings.py | 26 +++++------ SettingsPanel.py | 39 ++++++++++------ TableWidget.py | 87 +++++++++++++++++++++++++++++++++++- 4 files changed, 137 insertions(+), 41 deletions(-) diff --git a/Models/ExperimentSettings.py b/Models/ExperimentSettings.py index 490a3af..e1b3ace 100644 --- a/Models/ExperimentSettings.py +++ b/Models/ExperimentSettings.py @@ -160,7 +160,7 @@ def run_order(self): @run_order.setter def run_order(self, value): - self._run_order.set(value) + self._run_order.set(str(value)) @property def num_sans(self): @@ -168,7 +168,7 @@ def num_sans(self): @num_sans.setter def num_sans(self, value): - self._num_sans.set(value) + self._num_sans.set(int(value)) @property def num_trans(self): @@ -176,7 +176,7 @@ def num_trans(self): @num_trans.setter def num_trans(self, value): - self._num_trans.set(value) + self._num_trans.set(int(value)) @property def loop_per_run(self): @@ -184,7 +184,7 @@ def loop_per_run(self): @loop_per_run.setter def loop_per_run(self, value): - self._loop_per_run.set(value) + self._loop_per_run.set(str(value)) @property def a2_setting_sans(self): @@ -192,7 +192,7 @@ def a2_setting_sans(self): @a2_setting_sans.setter def a2_setting_sans(self, value): - self._a2_setting_sans.set(value) + self._a2_setting_sans.set(str(value)) @property def a2_setting_trans(self): @@ -200,7 +200,7 @@ def a2_setting_trans(self): @a2_setting_trans.setter def a2_setting_trans(self, value): - self._a2_setting_trans.set(value) + self._a2_setting_trans.set(str(value)) @property def sample_geometry(self): @@ -208,7 +208,7 @@ def sample_geometry(self): @sample_geometry.setter def sample_geometry(self, value): - self._sample_geometry.set(value) + self._sample_geometry.set(str(value)) self.sample_width_enabled = value != "Disc" @property @@ -217,9 +217,9 @@ def sample_height(self): @sample_height.setter def sample_height(self, value): - self._sample_height.set(value) + self._sample_height.set(float(value)) if not self.sample_width_enabled: - self.sample_width = value + self.sample_width = float(value) @property def sample_width(self): @@ -227,7 +227,7 @@ def sample_width(self): @sample_width.setter def sample_width(self, value): - self._sample_width.set(value) + self._sample_width.set(float(value)) @property def sample_width_enabled(self): @@ -235,7 +235,7 @@ def sample_width_enabled(self): @sample_width_enabled.setter def sample_width_enabled(self, value): - self._sample_width_enabled.set(value) + self._sample_width_enabled.set(float(value)) if not value: self.sample_width = self.sample_height @@ -245,7 +245,7 @@ def collection_mode(self): @collection_mode.setter def collection_mode(self, value): - self._collection_mode.set(value) + self._collection_mode.set(str(value)) @property def is_instrument(self): @@ -253,7 +253,7 @@ def is_instrument(self): @is_instrument.setter def is_instrument(self, value): - self.instrument.set(value) + self.instrument.set(str(value)) @property def is_valid(self): diff --git a/Properties/Settings.py b/Properties/Settings.py index 5bda93c..ba06326 100644 --- a/Properties/Settings.py +++ b/Properties/Settings.py @@ -12,7 +12,7 @@ def __init__(self, filename="Settings.ini"): @property def last_instrument(self): - return self.dict_settings.get("DEFAULT","LastInstrument") + return str(self.dict_settings.get("DEFAULT","LastInstrument")) @last_instrument.setter def last_instrument(self, value): @@ -21,7 +21,7 @@ def last_instrument(self, value): @property def sample_height(self): - return self.dict_settings.get("DEFAULT","SampleHeight") + return float(self.dict_settings.get("DEFAULT","SampleHeight")) @sample_height.setter def sample_height(self, value): @@ -30,7 +30,7 @@ def sample_height(self, value): @property def sample_width(self): - return self.dict_settings.get("DEFAULT","SampleWidth") + return float(self.dict_settings.get("DEFAULT","SampleWidth")) @sample_width.setter def sample_width(self, value): @@ -39,7 +39,7 @@ def sample_width(self, value): @property def sample_geometry(self): - return self.dict_settings.get("DEFAULT","Geometry") + return str(self.dict_settings.get("DEFAULT","Geometry")) @sample_geometry.setter def sample_geometry(self, value): @@ -48,7 +48,7 @@ def sample_geometry(self, value): @property def run_order(self): - return self.dict_settings.get("DEFAULT","RunOrder") + return str(self.dict_settings.get("DEFAULT","RunOrder")) @run_order.setter def run_order(self, value): @@ -57,7 +57,7 @@ def run_order(self, value): @property def num_trans(self): - return self.dict_settings.get("DEFAULT","NumTrans") + return int(self.dict_settings.get("DEFAULT","NumTrans")) @num_trans.setter def num_trans(self, value): @@ -66,7 +66,7 @@ def num_trans(self, value): @property def num_sans(self): - return self.dict_settings.get("DEFAULT","NumSans") + return int(self.dict_settings.get("DEFAULT","NumSans")) @num_sans.setter def num_sans(self, value): @@ -75,7 +75,7 @@ def num_sans(self, value): @property def loop_per_run(self): - return self.dict_settings.get("DEFAULT","LoopPerRun") + return str(self.dict_settings.get("DEFAULT","LoopPerRun")) @loop_per_run.setter def loop_per_run(self, value): @@ -84,7 +84,7 @@ def loop_per_run(self, value): @property def a2_setting_sans(self): - return self.dict_settings.get("DEFAULT","A2SettingSans") + return str(self.dict_settings.get("DEFAULT","A2SettingSans")) @a2_setting_sans.setter def a2_setting_sans(self, value): @@ -93,7 +93,7 @@ def a2_setting_sans(self, value): @property def a2_setting_trans(self): - return self.dict_settings.get("DEFAULT","A2SettingTrans") + return str(self.dict_settings.get("DEFAULT","A2SettingTrans")) @a2_setting_trans.setter def a2_setting_trans(self, value): @@ -102,7 +102,7 @@ def a2_setting_trans(self, value): @property def collection_mode(self): - return self.dict_settings.get("DEFAULT","CollectionMode") + return str(self.dict_settings.get("DEFAULT","CollectionMode")) @collection_mode.setter def collection_mode(self, value): @@ -111,7 +111,7 @@ def collection_mode(self, value): @property def count_rate(self): - return self.dict_settings.get("DEFAULT","CountRate") + return float(self.dict_settings.get("DEFAULT","CountRate")) @count_rate.setter def count_rate(self, value): @@ -120,7 +120,7 @@ def count_rate(self, value): @property def move_time(self): - return self.dict_settings.get("DEFAULT","MoveTime") + return float(self.dict_settings.get("DEFAULT","MoveTime")) @move_time.setter def move_time(self, value): diff --git a/SettingsPanel.py b/SettingsPanel.py index b75eb81..d3acda8 100644 --- a/SettingsPanel.py +++ b/SettingsPanel.py @@ -133,39 +133,50 @@ def create_widgets(self): # Define the Settings update methods def update_order(self, event): - self.settings.run_order = RunOrder.get_ordinal_from_string(self.com_order.get()) + self.settings.run_order = str(RunOrder.get_ordinal_from_string(self.com_order.get())) def update_num_sans(self, event): - self.settings.num_sans = self.txt_times_sans.get() + try: + self.settings.num_sans = int(self.txt_times_sans.get()) + except ValueError: + # Handle the error, e.g., show a message box or log the error + print("Invalid input for num_sans. Please enter an integer.") def update_num_trans(self, event): - self.settings.num_trans = self.txt_times_trans.get() + try: + self.settings.num_trans = int(self.txt_times_trans.get()) + except ValueError: + # Handle the error, e.g., show a message box or log the error + print("Invalid input for num_trans. Please enter an integer.") def update_loop_per_run(self, event): self.settings.loop_per_run = self.loop_run_var.get() def update_collection_mode(self, event): - self.settings.collection_mode = self.com_collection_mode.get() - - def update_collection_mode(self, event): - self.settings.collection_mode = self.com_collection_mode.get() + self.settings.collection_mode = str(self.com_collection_mode.get()) def update_a2_sans(self, event): - self.settings.a2_setting_sans = self.com_a2_sans.get() + self.settings.a2_setting_sans = str(self.com_a2_sans.get()) def update_a2_trans(self, event): - self.settings.a2_setting_trans = self.com_a2_trans.get() + self.settings.a2_setting_trans = str(self.com_a2_trans.get()) def update_sample_geometry(self, event): - self.settings.sample_geometry = Geometry.get_ordinal_from_string(self.com_sample_geo.get()) + self.settings.sample_geometry = str(Geometry.get_ordinal_from_string(self.com_sample_geo.get())) def update_sample_height(self, event): - self.settings.sample_height = self.txt_sample_height.get() + try: + self.settings.sample_height = float(self.txt_sample_height.get()) + except ValueError: + # Handle the error, e.g., show a message box or log the error + print("Invalid input for sample_height. Please enter a number.") def update_sample_width(self, event): - self.settings.sample_width = self.txt_sample_width.get() - - + try: + self.settings.sample_width = float(self.txt_sample_width.get()) + except ValueError: + # Handle the error, e.g., show a message box or log the error + print("Invalid input for sample_height. Please enter a number.") def btn_preview_script_click(self): if self.on_preview_clicked: diff --git a/TableWidget.py b/TableWidget.py index 784a117..6106bc7 100644 --- a/TableWidget.py +++ b/TableWidget.py @@ -8,7 +8,11 @@ from TableDataConstructor import TableDataConstructor TableEntries = "POSITION, TRANS, TRANS_WAIT, SANS, SANS_WAIT, PERIOD, SAMPLE_IDENTIFICATION_STRING, BACKGROUND, THICKNESS, TEMPERATURE, TEMPERATURE2, FIELD, SHEAR_RATE_1, SHEAR_RATE_2, SHEAR_ANGLE_1, SHEAR_ANGLE_2, PRE_COMMAND, POST_COMMAND, RB_NUMBER" - +#Get position of entries +entries = TableEntries.split(", ") +TRANS_WAIT_POS = entries.index("TRANS_WAIT") +SANS_WAIT_POS = entries.index("SANS_WAIT") + data=None @@ -289,6 +293,87 @@ def move_to_next_row(self, event): self.drawSelectedRect(self.currentrow, self.currentcol) self.drawSelectedRow() + def drawCellEntry(self, row, col, text=None): + if self.read_only: + return + h = self.rowheight + model = self.getModel() + cellvalue = model.getCellRecord(row, col) + #create dropdown list for Tr + if col == TRANS_WAIT_POS or col == SANS_WAIT_POS: + values = ['Option1', 'Option2', 'Option3'] + x1, y1, x2, y2 = self.getCellCoords(row, col) + w = x2 - x1 + + + def callback(value): + model.setValueAt(value, row, col) + self.drawText(row, col, value, 'black', align=self.align) + self.delete('entry') + + self.cellentry = Toplevel(self.parentframe) + self.cellentry.geometry(f'{w}x{h}+{x1}+{y1}') + self.cellentry.overrideredirect(1) + var = StringVar(value=cellvalue) + option_menu = OptionMenu(self.cellentry, var, *values, command=callback) + option_menu.pack() + self.cellentry.bind('', lambda e: self.cellentry.destroy()) + self.cellentry.focus_set() + else: + # Existing entry handling code + if Formula.isFormula(cellvalue): + return + else: + text = self.model.getValueAt(row, col) + x1,y1,x2,y2 = self.getCellCoords(row,col) + w=x2-x1 + #Draw an entry window + txtvar = StringVar() + txtvar.set(text) + def callback(e): + value = txtvar.get() + if value == '=': + #do a dialog that gets the formula into a text area + #then they can click on the cells they want + #when done the user presses ok and its entered into the cell + self.cellentry.destroy() + #its all done here.. + self.formula_Dialog(row, col) + return + + coltype = self.model.getColumnType(col) + if coltype == 'number': + sta = self.checkDataEntry(e) + if sta == 1: + model.setValueAt(value,row,col) + elif coltype == 'text': + model.setValueAt(value,row,col) + + color = self.model.getColorAt(row,col,'fg') + self.drawText(row, col, value, color, align=self.align) + if e.keysym=='Return': + self.delete('entry') + #self.drawRect(row, col) + #self.gotonextCell(e) + return + + self.cellentry=Entry(self.parentframe,width=20, + textvariable=txtvar, + #bg=self.entrybackgr, + #relief=FLAT, + takefocus=1, + font=self.thefont) + self.cellentry.icursor(END) + self.cellentry.bind('', callback) + self.cellentry.bind('', callback) + self.cellentry.focus_set() + self.entrywin=self.create_window(x1+self.inset,y1+self.inset, + width=w-self.inset*2,height=h-self.inset*2, + window=self.cellentry,anchor='nw', + tag='entry') + + return + def popupMenu(self, event, rows=None, cols=None, outside=None): """Add left and right click behaviour for canvas, should not have to override this function, it will take its values from defined dicts in constructor""" From 08a33bf39731441eb1c88ff20c51b70b74c6e496 Mon Sep 17 00:00:00 2001 From: Dirk Honecker Date: Tue, 7 Jan 2025 16:23:07 +0000 Subject: [PATCH 78/88] Add correct time choices --- TableWidget.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/TableWidget.py b/TableWidget.py index 6106bc7..ecbb86b 100644 --- a/TableWidget.py +++ b/TableWidget.py @@ -6,6 +6,7 @@ import random from collections import OrderedDict from TableDataConstructor import TableDataConstructor +from Models.WaitForUnits import WaitForUnits TableEntries = "POSITION, TRANS, TRANS_WAIT, SANS, SANS_WAIT, PERIOD, SAMPLE_IDENTIFICATION_STRING, BACKGROUND, THICKNESS, TEMPERATURE, TEMPERATURE2, FIELD, SHEAR_RATE_1, SHEAR_RATE_2, SHEAR_ANGLE_1, SHEAR_ANGLE_2, PRE_COMMAND, POST_COMMAND, RB_NUMBER" #Get position of entries @@ -301,22 +302,21 @@ def drawCellEntry(self, row, col, text=None): cellvalue = model.getCellRecord(row, col) #create dropdown list for Tr if col == TRANS_WAIT_POS or col == SANS_WAIT_POS: - values = ['Option1', 'Option2', 'Option3'] + values = WaitForUnits.choices x1, y1, x2, y2 = self.getCellCoords(row, col) w = x2 - x1 - + h = y2 - y1 def callback(value): model.setValueAt(value, row, col) self.drawText(row, col, value, 'black', align=self.align) self.delete('entry') - self.cellentry = Toplevel(self.parentframe) - self.cellentry.geometry(f'{w}x{h}+{x1}+{y1}') - self.cellentry.overrideredirect(1) + self.cellentry = Frame(self.parentframe, width=w, height=h) + self.cellentry.place(x=x1, y=y1) var = StringVar(value=cellvalue) option_menu = OptionMenu(self.cellentry, var, *values, command=callback) - option_menu.pack() + option_menu.grid(row=0, column=0, sticky="nsew") self.cellentry.bind('', lambda e: self.cellentry.destroy()) self.cellentry.focus_set() else: From 4fdeddb0d86e32c2f81cfd7e273d751987f45dd7 Mon Sep 17 00:00:00 2001 From: Dirk Honecker Date: Tue, 7 Jan 2025 17:40:03 +0000 Subject: [PATCH 79/88] Display estimated time --- EstimationPanel.py | 10 +- MainWindow.py | 2 + Models/Estimation.py | 4 +- Models/WaitForUnits.py | 2 +- Scripting/ScriptGenerator.py | 278 ++++++++++++++++++++--------------- 5 files changed, 166 insertions(+), 130 deletions(-) diff --git a/EstimationPanel.py b/EstimationPanel.py index 6b02877..fdecaf6 100644 --- a/EstimationPanel.py +++ b/EstimationPanel.py @@ -49,13 +49,15 @@ def create_widgets(self): # Est. script time ttk.Label(grid_frame, text="Est. script time:").grid(row=2, column=0, sticky=tk.E,padx=5, pady=5) script_time_frame = ttk.Frame(grid_frame) - script_time_frame.grid(row=2, column=1, columnspan=2,padx=5, pady=5) - self.txt_estimation = ttk.Entry(script_time_frame, width=15, textvariable=self.estimation.script_time, state="readonly") + script_time_frame.grid(row=2, column=1, columnspan=3,padx=5, pady=5) + self.txt_estimation = ttk.Entry(script_time_frame, width=15) + self.txt_estimation.insert(0, self.estimation.script_time) + self.txt_estimation.config(state="readonly") self.txt_estimation.grid(row=0, column=0,padx=5, pady=5) - + # Calculate button self.btn_calculate = ttk.Button(grid_frame, text="Calculate", command=self.btn_calculate_click) - self.btn_calculate.grid(row=3, column=1, columnspan=2,padx=5, pady=5) + self.btn_calculate.grid(row=2, column=3, padx=5, pady=5) # Define the Settings update methods diff --git a/MainWindow.py b/MainWindow.py index ec655ef..bad688d 100644 --- a/MainWindow.py +++ b/MainWindow.py @@ -171,10 +171,12 @@ def pnl_settings_on_preview_clicked(self): preview_dialog.show() def pnl_estimate_on_calculate_clicked(self): + self.script_generator= ScriptGenerator(self.settings) constructor = TableDataConstructor(TableEntries) self.data = constructor.construct_data(self.data_frame.table.model.getAllCells()) self.experiment_data=constructor.create_experiments(self.data.values()) self.estimation.script_time = self.script_generator.estimate_run_time(self.experiment_data, self.estimation) + self.update_estimation_panel() def btn_create_new_table_click(self): self.data =None diff --git a/Models/Estimation.py b/Models/Estimation.py index 2f99e71..2e36525 100644 --- a/Models/Estimation.py +++ b/Models/Estimation.py @@ -8,7 +8,7 @@ def __init__(self, settings=None): self.settings = settings if settings is not None else Settings() self._count_rate = tk.DoubleVar(value=self.settings.count_rate) self._move_time = tk.DoubleVar(value=self.settings.move_time) - self._script_time = tk.DoubleVar(value=0) + self._script_time = tk.StringVar(value="") self._is_valid = tk.BooleanVar(value=True) @@ -78,8 +78,6 @@ def validate(self, *args): error_message = "Count rate cannot be negative!" elif self.move_time < 0: error_message = "Move time cannot be negative!" - elif self.script_time < 0: - error_message = "Script time cannot be negative!" if error_message: self.is_valid = False else: diff --git a/Models/WaitForUnits.py b/Models/WaitForUnits.py index 0df9622..e43cecf 100644 --- a/Models/WaitForUnits.py +++ b/Models/WaitForUnits.py @@ -1,5 +1,5 @@ class WaitForUnits: - MICROAMPS = "Microamp-hours" + MICROAMPS = "uamps" MINUTES = "Minutes" SECONDS = "Seconds" FRAMES = "Frames" diff --git a/Scripting/ScriptGenerator.py b/Scripting/ScriptGenerator.py index aa69856..ef6238d 100644 --- a/Scripting/ScriptGenerator.py +++ b/Scripting/ScriptGenerator.py @@ -33,34 +33,38 @@ def generate_header(self, scriptname): script = "import sys\n" script += "from genie_python import genie as g, BLOCK_NAMES as b\n" script += "sys.path.append(r\"c:\\instrument\\scripts\")\n" - - if self.instrument == "LARMOR": - script += "from instrument.larmor import * # pylint: disable=wildcard-import, unused-wildcard-import\n" - if self.instrument == "LOQ": - script += "from instrument.loq import * # pylint: disable=wildcard-import, unused-wildcard-import\n" - if self.instrument == "ZOOM": - script += "from instrument.zoom import * # pylint: disable=wildcard-import, unused-wildcard-import\n" - if self.instrument == "SANS2D": - script += "from instrument.sans2d import * # pylint: disable=wildcard-import, unused-wildcard-import\n" - + script += self.import_instrument_module() script += f"\ndef {scriptname}():\n" return script + def import_instrument_module(self): + instrument_modules = { + "LARMOR": "from instrument.larmor import * # pylint: disable=wildcard-import, unused-wildcard-import\n", + "LOQ": "from instrument.loq import * # pylint: disable=wildcard-import, unused-wildcard-import\n", + "ZOOM": "from instrument.zoom import * # pylint: disable=wildcard-import, unused-wildcard-import\n", + "SANS2D": "from instrument.sans2d import * # pylint: disable=wildcard-import, unused-wildcard-import\n" + } + return instrument_modules.get(self.instrument, "") + def generate_setup(self): script = "" - script += self.get_indent_string(1) + f"g.change_sample_par('Width', '{self.settings.sample_width}')\n" - script += self.get_indent_string(1) + f"g.change_sample_par('height', '{self.settings.sample_height}')\n" - script += self.get_indent_string(1) + f"g.change_sample_par('Geometry', '{self.settings.sample_geometry}')\n\n" + script += self.change_sample_parameter('Width', self.settings.sample_width) + script += self.change_sample_parameter('height', self.settings.sample_height) + script += self.change_sample_parameter('Geometry', self.settings.sample_geometry) return script + def change_sample_parameter(self, param, value): + return self.get_indent_string(1) + f"g.change_sample_par('{param}', '{value}')\n" + + def check_positions(self, runs): script = "" - if self.instrument == "LARMOR": - position_list = ",".join([exp.position for exp in runs if exp.position]) - if position_list: - script += self.get_indent_string(1) + f"if check_move_pos(['{position_list}']) != True:\n" - script += self.get_indent_string(2) + "print('Invalid positions - script aborted')\n" - script += self.get_indent_string(2) + "return\n\n" + + position_list = ",".join([exp.position for exp in runs if exp.position]) + if position_list: + script += self.get_indent_string(1) + f"if check_move_pos(['{position_list}']) != True:\n" + script += self.get_indent_string(2) + "print('Invalid positions - script aborted')\n" + script += self.get_indent_string(2) + "return\n\n" return script def generate_runs(self, runs): @@ -69,131 +73,144 @@ def generate_runs(self, runs): return script if float(self.settings.run_order) == RunOrder.ALL_TRANS[0]: + script += self.generate_all_trans_runs(runs) + elif float(self.settings.run_order) == RunOrder.ALL_SANS[0]: + script += self.generate_all_sans_runs(runs) + else: + script += self.generate_mixed_runs(runs) + return script + + def generate_all_trans_runs(self, runs): + script = self.get_indent_string(1) + f"for i in range({self.settings.num_trans}):\n" + for exp in runs: + if exp.trans: + script = self.do_trans_run(script, exp, 2) + script += self.get_indent_string(1) + f"for i in range({self.settings.num_sans}):\n" + for exp in runs: + if exp.sans: + script = self.do_sans_run(script, exp, 2) + return script + + def generate_all_sans_runs(self, runs): + script = self.get_indent_string(1) + f"for i in range({self.settings.num_sans}):\n" + for exp in runs: + if exp.sans: + script = self.do_sans_run(script, exp, 2) + if self.settings.num_trans and any(exp.trans for exp in runs): script += self.get_indent_string(1) + f"for i in range({self.settings.num_trans}):\n" for exp in runs: if exp.trans: script = self.do_trans_run(script, exp, 2) - script += self.get_indent_string(1) + f"for i in range({self.settings.num_sans}):\n" - for exp in runs: - if exp.sans: - script = self.do_sans_run(script, exp, 2) - elif float(self.settings.run_order) == RunOrder.ALL_SANS[0]: - script += self.get_indent_string(1) + f"for i in range({self.settings.num_sans}):\n" - for exp in runs: - if exp.sans: - script = self.do_sans_run(script, exp, 2) - if self.settings.num_trans and any(exp.trans for exp in runs): - script += self.get_indent_string(1) + f"for i in range({self.settings.num_trans}):\n" - for exp in runs: - if exp.trans: - script = self.do_trans_run(script, exp, 2) - else: - script += self.get_indent_string(1) + f"num_sans = {self.settings.num_sans}\n" - script += self.get_indent_string(1) + f"num_trans = {self.settings.num_trans}\n" - script += self.get_indent_string(1) + "count = 0\n" - script += self.get_indent_string(1) + "while True:\n" - indent = 2 - for exp in runs: - if float(self.settings.run_order) == RunOrder.TRANS_FIRST[0]: - if exp.trans: - script += self.get_indent_string(indent) + "if count < num_trans:\n" - script = self.do_trans_run(script, exp, indent + 1) - if exp.sans: - script += self.get_indent_string(indent) + "if count < num_sans:\n" - script = self.do_sans_run(script, exp, indent + 1) - else: - if exp.sans: - script += self.get_indent_string(indent) + "if count < num_sans:\n" - script = self.do_sans_run(script, exp, indent + 1) - if exp.trans: - script += self.get_indent_string(indent) + "if count < num_trans:\n" - script = self.do_trans_run(script, exp, indent + 1) - script += self.get_indent_string(indent) + "count += 1\n" - script += self.get_indent_string(indent) + "if count >= num_trans and count >= num_sans: break\n" + return script + + def generate_mixed_runs(self, runs): + script = ( + self.get_indent_string(1) + f"num_sans = {self.settings.num_sans}\n" + + self.get_indent_string(1) + f"num_trans = {self.settings.num_trans}\n" + + self.get_indent_string(1) + "count = 0\n" + + self.get_indent_string(1) + "while True:\n" + ) + indent = 2 + for exp in runs: + if float(self.settings.run_order) == RunOrder.TRANS_FIRST[0]: + script = self.add_trans_first_runs(script, exp, indent) + else: + script = self.add_sans_first_runs(script, exp, indent) + script += self.get_indent_string(indent) + "count += 1\n" + script += self.get_indent_string(indent) + "if count >= num_trans and count >= num_sans: break\n" + return script + + def add_trans_first_runs(self, script, exp, indent): + if exp.trans: + script += self.get_indent_string(indent) + "if count < num_trans:\n" + script = self.do_trans_run(script, exp, indent + 1) + if exp.sans: + script += self.get_indent_string(indent) + "if count < num_sans:\n" + script = self.do_sans_run(script, exp, indent + 1) + return script + + def add_sans_first_runs(self, script, exp, indent): + if exp.sans: + script += self.get_indent_string(indent) + "if count < num_sans:\n" + script = self.do_sans_run(script, exp, indent + 1) + if exp.trans: + script += self.get_indent_string(indent) + "if count < num_trans:\n" + script = self.do_trans_run(script, exp, indent + 1) return script def do_trans_run(self, script, exp, indent): script += self.do_sample_environment(exp, indent) - waitfor = "uamps" - rtype = "histogram" - fulltitle = f"{{{exp.sample}}}" - if self.settings.collection_mode == DataCollection.EVENTS: - rtype = DataCollection.EVENTS - if exp.trans_wait == "Frames": - waitfor = "frames" - elif exp.trans_wait == "Seconds": - waitfor = "seconds" - elif exp.trans_wait == "Minutes": - waitfor = "minutes" - if self.instrument == "LARMOR" or self.instrument == "ZOOM": - script += self.get_indent_string(indent) + f"do_trans(title='{fulltitle}', pos='{exp.position}', {waitfor}={exp.trans}, thickness={exp.thickness})\n" - elif self.instrument == "LOQ": - script += self.get_indent_string(indent) + f"do_trans(title='{fulltitle}', pos='{exp.position}', {waitfor}={exp.trans}, thickness={exp.thickness}, aperture='small')\n" - else: - script += self.get_indent_string(indent) + f"do_trans(title='{fulltitle}', pos='{exp.position}', {waitfor}={exp.trans}, thickness={exp.thickness}, dae='transmission', aperture='{self.aperture_string(self.settings.a2_setting_trans)}')\n" + waitfor, rtype, fulltitle = self.get_run_parameters(exp, "trans") + script += self.get_trans_run_command(exp, indent, waitfor, fulltitle) script += self.get_indent_string(indent) + exp.post_command + "\n" return script def do_sans_run(self, script, exp, indent): script += self.do_sample_environment(exp, indent) + waitfor, rtype, fulltitle = self.get_run_parameters(exp, "sans") + script += self.get_sans_run_command(exp, indent, waitfor, fulltitle) + script += self.get_indent_string(indent) + exp.post_command + "\n" + return script + + def get_run_parameters(self, exp, run_type): waitfor = "uamps" rtype = "histogram" fulltitle = f"{{{exp.sample}}}" - if exp.background: - fulltitle += f"_{{{exp.background}}}" - if self.settings.collection_mode in ["Events", "TRANSSANS_Events"]: - rtype = "event" - elif self.settings.collection_mode == "SESANS": - rtype = "sesans" - if self.settings.collection_mode in ["TRANSSANS_Events", "TRANSSANS_Histogram"]: - fulltitle += "_TRANS" - if exp.sans_wait == "Frames": - waitfor = "frames" - elif exp.sans_wait == "Seconds": - waitfor = "seconds" - elif exp.sans_wait == "Minutes": - waitfor = "minutes" - if self.instrument == "LARMOR" or self.instrument == "ZOOM": - script += self.get_indent_string(indent) + f"do_sans(title='{fulltitle}', pos='{exp.position}', {waitfor}={exp.sans}, thickness={exp.thickness}, dae='{rtype}')\n" + if run_type == "trans": + if exp.trans_wait == "Frames": + waitfor = "frames" + elif exp.trans_wait == "Seconds": + waitfor = "seconds" + elif exp.trans_wait == "Minutes": + waitfor = "minutes" else: - script += self.get_indent_string(indent) + f"do_sans(title='{fulltitle}', pos='{exp.position}', {waitfor}={exp.sans}, thickness={exp.thickness}, dae='{rtype}', aperture='{self.aperture_string(self.settings.a2_setting_sans)}')\n" - script += self.get_indent_string(indent) + exp.post_command + "\n" - return script + if exp.background: + fulltitle += f"_{{{exp.background}}}" + if self.settings.collection_mode in ["Events", "TRANSSANS_Events"]: + rtype = "event" + elif self.settings.collection_mode == "SESANS": + rtype = "sesans" + if self.settings.collection_mode in ["TRANSSANS_Events", "TRANSSANS_Histogram"]: + fulltitle += "_TRANS" + if exp.sans_wait == "Frames": + waitfor = "frames" + elif exp.sans_wait == "Seconds": + waitfor = "seconds" + elif exp.sans_wait == "Minutes": + waitfor = "minutes" + return waitfor, rtype, fulltitle + + def get_trans_run_command(self, exp, indent, waitfor, fulltitle): + if self.instrument in ["LARMOR", "ZOOM"]: + return self.get_indent_string(indent) + f"do_trans(title='{fulltitle}', pos='{exp.position}', {waitfor}={exp.trans}, thickness={exp.thickness})\n" + elif self.instrument == "LOQ": + return self.get_indent_string(indent) + f"do_trans(title='{fulltitle}', pos='{exp.position}', {waitfor}={exp.trans}, thickness={exp.thickness}, aperture='small')\n" + else: + return self.get_indent_string(indent) + f"do_trans(title='{fulltitle}', pos='{exp.position}', {waitfor}={exp.trans}, thickness={exp.thickness}, dae='transmission', aperture='{self.aperture_string(self.settings.a2_setting_trans)}')\n" + + def get_sans_run_command(self, exp, indent, waitfor, fulltitle): + if self.instrument in ["LARMOR", "ZOOM"]: + return self.get_indent_string(indent) + f"do_sans(title='{fulltitle}', pos='{exp.position}', {waitfor}={exp.sans}, thickness={exp.thickness}, dae='histogram')\n" + else: + return self.get_indent_string(indent) + f"do_sans(title='{fulltitle}', pos='{exp.position}', {waitfor}={exp.sans}, thickness={exp.thickness}, dae='histogram', aperture='{self.aperture_string(self.settings.a2_setting_sans)}')\n" + def aperture_string(self, size): - - if size in [A2Setting.LARGE, A2Setting.LARMOR_LARGE]: - return "large" - elif size in [A2Setting.MEDIUM, A2Setting.LARMOR_MEDIUM]: - return "medium" - elif size in [A2Setting.SMALL, A2Setting.LARMOR_SMALL]: - return "small" - elif size in [A2Setting.X_LARGE]: - return "xlarge" - return "" + aperture_sizes = { + A2Setting.LARGE: "large", + A2Setting.LARMOR_LARGE: "large", + A2Setting.MEDIUM: "medium", + A2Setting.LARMOR_MEDIUM: "medium", + A2Setting.SMALL: "small", + A2Setting.LARMOR_SMALL: "small", + A2Setting.X_LARGE: "xlarge" + } + return aperture_sizes.get(size, "") def do_sample_environment(self, exp, indent): script = "" - if exp.temperature1: - temp1 = float(exp.temperature1) - temp1low = temp1 - 1.0 - temp1high = temp1 + 1.0 - if self.is_loq: - script += self.get_indent_string(indent) + f"g.cset(internal_setpoint_1={exp.temperature1})\n" - script += self.get_indent_string(indent) + f"g.cset(\"Julabo_1_ext\", lowlimit={temp1low}, highlimit={temp1high}, runcontrol=True)\n" - else: - script += self.get_indent_string(indent) + f"g.cset(JULABO1_SP={exp.temperature1})\n" - script += self.get_indent_string(indent) + f"g.cset(\"JULABO1_TEMP\", lowlimit={temp1low}, highlimit={temp1high}, runcontrol=True)\n" - if exp.temperature2: - temp2 = float(exp.temperature2) - temp2low = temp2 - 1.0 - temp2high = temp2 + 1.0 - if self.is_loq: - script += self.get_indent_string(indent) + f"g.cset(internal_setpoint_2={exp.temperature2})\n" - script += self.get_indent_string(indent) + f"g.cset(\"Julabo_2_ext\", lowlimit={temp2low}, highlimit={temp2high}, runcontrol=True)\n" - else: - script += self.get_indent_string(indent) + f"g.cset(JULABO2_SP={exp.temperature2})\n" - script += self.get_indent_string(indent) + f"g.cset(\"JULABO2_TEMP\", lowlimit={temp2low}, highlimit={temp2high}, runcontrol=True)\n" + script += self.set_temperature(exp.temperature1, "JULABO1", indent) + script += self.set_temperature(exp.temperature2, "JULABO2", indent) if exp.pre_command: script += self.get_indent_string(indent) + exp.pre_command + "\n" if exp.field: @@ -208,6 +225,23 @@ def do_sample_environment(self, exp, indent): script += self.get_indent_string(indent) + f"g.cset(SHEAR_ANGLE_2={exp.shear_angle2})\n" return script + def set_temperature(self, temperature, julabo, indent): + if temperature: + temp = float(temperature) + temp_low = temp - 1.0 + temp_high = temp + 1.0 + if self.is_loq: + return ( + self.get_indent_string(indent) + f"g.cset(internal_setpoint_{julabo[-1]}={temperature})\n" + + self.get_indent_string(indent) + f"g.cset(\"Julabo_{julabo[-1]}_ext\", lowlimit={temp_low}, highlimit={temp_high}, runcontrol=True)\n" + ) + else: + return ( + self.get_indent_string(indent) + f"g.cset({julabo}_SP={temperature})\n" + + self.get_indent_string(indent) + f"g.cset(\"{julabo}_TEMP\", lowlimit={temp_low}, highlimit={temp_high}, runcontrol=True)\n" + ) + return "" + def estimate_run_time(self, runs, estimation): minutes = 0 if self.settings.num_sans > 0: From 0026c9bf2c70061c4ebb2003970666a187c5d9b9 Mon Sep 17 00:00:00 2001 From: Dirk Honecker Date: Wed, 8 Jan 2025 18:13:41 +0000 Subject: [PATCH 80/88] Create dropdown list for background selection --- TableWidget.py | 37 ++++++++++++++++++++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-) diff --git a/TableWidget.py b/TableWidget.py index ecbb86b..ae484b3 100644 --- a/TableWidget.py +++ b/TableWidget.py @@ -13,7 +13,15 @@ entries = TableEntries.split(", ") TRANS_WAIT_POS = entries.index("TRANS_WAIT") SANS_WAIT_POS = entries.index("SANS_WAIT") - + +BACK_POS = entries.index("BACKGROUND") +SAMPLE_POS = entries.index("SAMPLE_IDENTIFICATION_STRING") + +''' +Check if this allows to Identify the column indices for "back" and "sample" +back_col = self.model.columnNames.index("back") +sample_col = self.model.columnNames.index("sample") +''' data=None @@ -307,6 +315,33 @@ def drawCellEntry(self, row, col, text=None): w = x2 - x1 h = y2 - y1 + def callback(value): + model.setValueAt(value, row, col) + self.drawText(row, col, value, 'black', align=self.align) + self.delete('entry') + + self.cellentry = Frame(self.parentframe, width=w, height=h) + self.cellentry.place(x=x1, y=y1) + var = StringVar(value=cellvalue) + option_menu = OptionMenu(self.cellentry, var, *values, command=callback) + option_menu.grid(row=0, column=0, sticky="nsew") + self.cellentry.bind('', lambda e: self.cellentry.destroy()) + self.cellentry.focus_set() + elif col == BACK_POS: + # Collect unique, non-empty strings from the "sample" column + values = set() + for r in range(self.model.getRowCount()): + value = self.model.getValueAt(r, SAMPLE_POS) + if value: + values.add(value) + values = list(values) + if not values: + return + + x1, y1, x2, y2 = self.getCellCoords(row, col) + w = x2 - x1 + h = y2 - y1 + def callback(value): model.setValueAt(value, row, col) self.drawText(row, col, value, 'black', align=self.align) From b84d2a7d0232caddab030eecabbbf172302f7c7b Mon Sep 17 00:00:00 2001 From: Dirk Honecker Date: Wed, 15 Jan 2025 13:02:18 +0000 Subject: [PATCH 81/88] Pyinstaller settings, and catching error with new commands popupmenu --- MainWindow.py | 1 - TableWidget.py | 5 ++++- app.spec | 44 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 48 insertions(+), 2 deletions(-) create mode 100644 app.spec diff --git a/MainWindow.py b/MainWindow.py index bad688d..a90c08d 100644 --- a/MainWindow.py +++ b/MainWindow.py @@ -4,7 +4,6 @@ from Models.DataCollection import DataCollection from Models.InstrumentInfo import InstrumentInfo from Models.Estimation import Estimation -from IO.ExcelIO import ExcelIO from IO.CsvExporter import CsvExporter from Dialogs.PreviewDialog import PreviewDialog from SettingsPanel import SettingsPanel diff --git a/TableWidget.py b/TableWidget.py index ae484b3..5dffda5 100644 --- a/TableWidget.py +++ b/TableWidget.py @@ -478,7 +478,10 @@ def add_defaultcommands(): add_defaultcommands() for action in general: - popupmenu.add_command(label=action, command=defaultactions[action]) + if action in defaultactions: + popupmenu.add_command(label=action, command=defaultactions[action]) + else: + print(f"Warning: No default action defined for '{action}'") popupmenu.add_separator() popupmenu.bind("", popupFocusOut) diff --git a/app.spec b/app.spec new file mode 100644 index 0000000..fbada23 --- /dev/null +++ b/app.spec @@ -0,0 +1,44 @@ +# -*- mode: python ; coding: utf-8 -*- + + +a = Analysis( + ['app.py'], + pathex=[], + binaries=[], + datas=[], + hiddenimports=[], + hookspath=[], + hooksconfig={}, + runtime_hooks=[], + excludes=[], + noarchive=False, + optimize=0, +) +pyz = PYZ(a.pure) + +exe = EXE( + pyz, + a.scripts, + [], + exclude_binaries=True, + name='ISIS SANS Script Writer', + debug=True, + bootloader_ignore_signals=False, + strip=False, + upx=True, + console=True, + disable_windowed_traceback=False, + argv_emulation=False, + target_arch=None, + codesign_identity=None, + entitlements_file=None, +) +coll = COLLECT( + exe, + a.binaries, + a.datas, + strip=False, + upx=True, + upx_exclude=[], + name='app', +) From 6c1cb973cfe57d1f1ef64e6710f52f2f39284cfc Mon Sep 17 00:00:00 2001 From: Dirk Honecker Date: Thu, 13 Aug 2026 14:29:25 +0100 Subject: [PATCH 82/88] fix merge conflict --- README.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/README.md b/README.md index 2593c16..424f02a 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,17 @@ + # SANS_Script The script generator designed for use on LARMOR, SANS2D and LOQ. + +The folder /SANS_Script_GUI is the original version written in C# +The folder /Python_SANS_script_GUI is a rewrite in Python. It should allow easier maintainability and faster implementation of new features as Python is a more familiar language. + +## Usage + +1) Clone the repository: +git clone https://github.com/dehoni/SANS_Script_GUI.git +cd Python_SANS_Script_GUI + +2) Start the script writer: +python app.py + + From 108aa0b8cf0cb255e45b2ea90bbcf8945c1e10d7 Mon Sep 17 00:00:00 2001 From: Dirk Honecker Date: Thu, 13 Aug 2026 14:30:16 +0100 Subject: [PATCH 83/88] resolve merge conflict --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index 424f02a..e143790 100644 --- a/README.md +++ b/README.md @@ -14,4 +14,8 @@ cd Python_SANS_Script_GUI 2) Start the script writer: python app.py +3) to build the app: + pyinstaller app.spec + + The executable can be found in the "dist" folder From 169eb847cbdbe3eb6d302735b121fe50ab8e44c2 Mon Sep 17 00:00:00 2001 From: Dirk Honecker Date: Thu, 13 Aug 2026 14:30:49 +0100 Subject: [PATCH 84/88] resolve merge conflict --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index e143790..1aaf657 100644 --- a/README.md +++ b/README.md @@ -19,3 +19,6 @@ python app.py The executable can be found in the "dist" folder + add settings.ini to the "_internal" folder + run "SANS script Writer.exe" + From 3e92b1f063182c6e4eb108575e2a7924b4791566 Mon Sep 17 00:00:00 2001 From: Dirk Honecker Date: Thu, 13 Aug 2026 14:31:59 +0100 Subject: [PATCH 85/88] resolve merge conflict --- .gitignore | 5 +++++ EstimationPanel.py | 2 +- MainWindow.py | 2 +- Models/Estimation.py | 2 +- Models/ExperimentSettings.py | 4 ++-- Models/InstrumentInfo.py | 2 +- Scripting/ScriptGenerator.py | 2 +- Properties/Settings.ini => Settings.ini | 12 ++++++------ Properties/Settings.py => Settings.py | 0 SettingsPanel.py | 2 +- app.spec | 7 ++++--- 11 files changed, 23 insertions(+), 17 deletions(-) rename Properties/Settings.ini => Settings.ini (75%) rename Properties/Settings.py => Settings.py (100%) diff --git a/.gitignore b/.gitignore index 1a9e069..96ab602 100644 --- a/.gitignore +++ b/.gitignore @@ -466,3 +466,8 @@ MigrationBackup/ # Fody - auto-generated XML schema FodyWeavers.xsd + +/build/app +/dist/app +/executable + diff --git a/EstimationPanel.py b/EstimationPanel.py index fdecaf6..07c6d24 100644 --- a/EstimationPanel.py +++ b/EstimationPanel.py @@ -1,7 +1,7 @@ import tkinter as tk from tkinter import ttk from Models.Estimation import Estimation -from Properties.Settings import Settings +from Settings import Settings class EstimationPanel(ttk.Frame): def __init__(self, parent, estimation = None, settings = None): diff --git a/MainWindow.py b/MainWindow.py index a90c08d..98a5f15 100644 --- a/MainWindow.py +++ b/MainWindow.py @@ -9,7 +9,7 @@ from SettingsPanel import SettingsPanel from EstimationPanel import EstimationPanel from TableWidget import TableWidget -from Properties.Settings import Settings +from Settings import Settings from Scripting.ScriptGenerator import ScriptGenerator from Models.ExperimentSettings import ExperimentSettings from TableDataConstructor import TableDataConstructor diff --git a/Models/Estimation.py b/Models/Estimation.py index 2e36525..40b571e 100644 --- a/Models/Estimation.py +++ b/Models/Estimation.py @@ -1,5 +1,5 @@ import tkinter as tk -from Properties.Settings import Settings +from Settings import Settings diff --git a/Models/ExperimentSettings.py b/Models/ExperimentSettings.py index e1b3ace..aab40ae 100644 --- a/Models/ExperimentSettings.py +++ b/Models/ExperimentSettings.py @@ -1,9 +1,9 @@ import tkinter as tk -from Properties.Settings import Settings +from Settings import Settings from Models.A2Setting import A2Setting from Models.DataCollection import DataCollection from Models.Estimation import Estimation -from Properties.Settings import Settings +from Settings import Settings from Models.RunOrder import RunOrder from Models.Geometry import Geometry diff --git a/Models/InstrumentInfo.py b/Models/InstrumentInfo.py index 6ad59ee..c3f3942 100644 --- a/Models/InstrumentInfo.py +++ b/Models/InstrumentInfo.py @@ -1,7 +1,7 @@ import tkinter as tk from tkinter import StringVar, BooleanVar from typing import List -#from Properties.Settings import Settings +#from Settings import Settings from Models.ExperimentSettings import ExperimentSettings class InstrumentInfo: diff --git a/Scripting/ScriptGenerator.py b/Scripting/ScriptGenerator.py index ef6238d..02e998e 100644 --- a/Scripting/ScriptGenerator.py +++ b/Scripting/ScriptGenerator.py @@ -3,7 +3,7 @@ from datetime import datetime, timedelta from Models.Estimation import Estimation from Models.Experiment import Experiment -from Properties.Settings import Settings +from Settings import Settings from Models.ExperimentSettings import ExperimentSettings from Models.A2Setting import A2Setting from Models.RunOrder import RunOrder diff --git a/Properties/Settings.ini b/Settings.ini similarity index 75% rename from Properties/Settings.ini rename to Settings.ini index 8c9537d..a6c0b47 100644 --- a/Properties/Settings.ini +++ b/Settings.ini @@ -1,14 +1,14 @@ [DEFAULT] lastinstrument = LOQ -sampleheight = 12 -samplewidth = 12 +sampleheight = 10 +samplewidth = 2 geometry = 4 -runorder = 3 -numtrans = 5 +runorder = 2 +numtrans = 3 numsans = 1 loopperrun = True -a2settingsans = Large = 17 mm -a2settingtrans = Large = 17 mm +a2settingsans = Large (17 mm) +a2settingtrans = Large (17 mm) collectionmode = Events tableentries = "POSITION, TRANS, TRANS_WAIT, SANS, SANS_WAIT, PERIOD, SAMPLE_IDENTIFICATION_STRING, BACKGROUND, THICKNESS, TEMPERATURE, TEMPERATURE2, FIELD, SHEAR_RATE_1, SHEAR_RATE_2, SHEAR_ANGLE_1, SHEAR_ANGLE_2, PRE_COMMAND, POST_COMMAND, RB_NUMBER" countrate = 170 diff --git a/Properties/Settings.py b/Settings.py similarity index 100% rename from Properties/Settings.py rename to Settings.py diff --git a/SettingsPanel.py b/SettingsPanel.py index d3acda8..4368376 100644 --- a/SettingsPanel.py +++ b/SettingsPanel.py @@ -1,6 +1,6 @@ import tkinter as tk from tkinter import ttk -from Properties.Settings import Settings +from Settings import Settings from Models.RunOrder import RunOrder from Models.Geometry import Geometry from Models.ExperimentSettings import ExperimentSettings diff --git a/app.spec b/app.spec index fbada23..3c5a556 100644 --- a/app.spec +++ b/app.spec @@ -21,12 +21,13 @@ exe = EXE( a.scripts, [], exclude_binaries=True, - name='ISIS SANS Script Writer', - debug=True, + name='SANS_ScriptWriter', + debug=False, bootloader_ignore_signals=False, strip=False, upx=True, - console=True, + console=False, + icon='SANS_Icon.ico', disable_windowed_traceback=False, argv_emulation=False, target_arch=None, From 2a129435d850632d7a59d15fb1f019b367a7d05a Mon Sep 17 00:00:00 2001 From: Dirk Honecker Date: Thu, 13 Aug 2026 14:33:02 +0100 Subject: [PATCH 86/88] resolve merge conflict --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index 1aaf657..6d05a13 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,12 @@ +<<<<<<< HEAD # SANS_Script The script generator designed for use on LARMOR, SANS2D and LOQ. The folder /SANS_Script_GUI is the original version written in C# The folder /Python_SANS_script_GUI is a rewrite in Python. It should allow easier maintainability and faster implementation of new features as Python is a more familiar language. +The python version uses the Python packages Tkintertable, openpyxl. + ## Usage From 5846b3125164f77f73b276a66c59cb264a12b771 Mon Sep 17 00:00:00 2001 From: Dirk Honecker Date: Thu, 13 Aug 2026 13:50:12 +0100 Subject: [PATCH 87/88] clean up test file --- TextFile2.py | 7 ++----- Transcript_GitCopilot.txt | 3 +++ 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/TextFile2.py b/TextFile2.py index 100c904..1265f3a 100644 --- a/TextFile2.py +++ b/TextFile2.py @@ -3,9 +3,6 @@ from tkinter.scrolledtext import ScrolledText import pandas as pd from Models.DataCollection import DataCollection -from ViewModels.DataGridVM import DataGridVM -#from ViewModels.SettingsPanelVM import SettingsPanelVM -#from ViewModels.InstrumentVM import InstrumentVM from Models.InstrumentInfo import InstrumentInfo from Models.Estimation import Estimation from Models.ExperimentSettings import ExperimentSettings @@ -15,7 +12,7 @@ from SettingsPanel import SettingsPanel from EstimationPanel import EstimationPanel from TableWidget import TableWidget -from Properties.Settings import Settings +from Settings import Settings from Scripting.ScriptGenerator import ScriptGenerator from datetime import datetime, timedelta @@ -30,7 +27,7 @@ settings = ExperimentSettings() settings(sample_width=10, sample_height=10, sample_geometry="Flat ", order="AllTrans", num_trans=1, num_sans=1, collection_mode="Events", a2_setting_trans="Large", a2_setting_sans="Large") - estimation = Estimation(count_rate=100, move_time=10) + estimation = Estimation(settings=settings) instrument = "LARMOR" generator = ScriptGenerator(instrument) script = generator.generate_script(runs, settings, "my_script") diff --git a/Transcript_GitCopilot.txt b/Transcript_GitCopilot.txt index f0585ac..76aa41c 100644 --- a/Transcript_GitCopilot.txt +++ b/Transcript_GitCopilot.txt @@ -1,3 +1,6 @@ +to create exe +https://github.com/oop7/Py-to-EXE-Guideau + dehoni: @workspace /explain Workspace: The provided code is a C# file named From 3210026e04f598fd883569afdb13e6066d9f7ce6 Mon Sep 17 00:00:00 2001 From: Dirk Honecker Date: Thu, 13 Aug 2026 13:58:40 +0100 Subject: [PATCH 88/88] create folder Python_SANS_Script_GUI --- App.py => Python_SANS_Script_GUI/App.py | 0 .../Dialogs}/PreviewDialog.py | 0 .../EstimationPanel.py | 0 .../IO}/CsvExporter.py | 0 {IO => Python_SANS_Script_GUI/IO}/ExcelIO.py | 0 .../MainWindow.py | 0 .../Models}/A2Setting.py | 0 .../Models}/DataCollection.py | 0 .../Models}/Estimation.py | 0 .../Models}/Experiment.py | 0 .../Models}/ExperimentSettings.py | 0 .../Models}/Geometry.py | 0 .../Models}/InstrumentInfo.py | 0 .../Models}/RunOrder.py | 0 .../Models}/WaitForUnits.py | 0 .../Properties}/AssemblyInfo.py | 0 .../SANS_Icon.ico | Bin .../Scripting}/ScriptGenerator.py | 0 .../Settings.ini | 0 .../Settings.py | 0 .../SettingsPanel.py | 0 .../TableDataConstructor.py | 0 .../TableTest.py | 0 .../TableWidget.py | 0 .../TextFile2.py | 0 app.spec => Python_SANS_Script_GUI/app.spec | 0 Transcript_GitCopilot.txt | 3940 ----------------- 27 files changed, 3940 deletions(-) rename App.py => Python_SANS_Script_GUI/App.py (100%) rename {Dialogs => Python_SANS_Script_GUI/Dialogs}/PreviewDialog.py (100%) rename EstimationPanel.py => Python_SANS_Script_GUI/EstimationPanel.py (100%) rename {IO => Python_SANS_Script_GUI/IO}/CsvExporter.py (100%) rename {IO => Python_SANS_Script_GUI/IO}/ExcelIO.py (100%) rename MainWindow.py => Python_SANS_Script_GUI/MainWindow.py (100%) rename {Models => Python_SANS_Script_GUI/Models}/A2Setting.py (100%) rename {Models => Python_SANS_Script_GUI/Models}/DataCollection.py (100%) rename {Models => Python_SANS_Script_GUI/Models}/Estimation.py (100%) rename {Models => Python_SANS_Script_GUI/Models}/Experiment.py (100%) rename {Models => Python_SANS_Script_GUI/Models}/ExperimentSettings.py (100%) rename {Models => Python_SANS_Script_GUI/Models}/Geometry.py (100%) rename {Models => Python_SANS_Script_GUI/Models}/InstrumentInfo.py (100%) rename {Models => Python_SANS_Script_GUI/Models}/RunOrder.py (100%) rename {Models => Python_SANS_Script_GUI/Models}/WaitForUnits.py (100%) rename {Properties => Python_SANS_Script_GUI/Properties}/AssemblyInfo.py (100%) rename SANS_Icon.ico => Python_SANS_Script_GUI/SANS_Icon.ico (100%) rename {Scripting => Python_SANS_Script_GUI/Scripting}/ScriptGenerator.py (100%) rename Settings.ini => Python_SANS_Script_GUI/Settings.ini (100%) rename Settings.py => Python_SANS_Script_GUI/Settings.py (100%) rename SettingsPanel.py => Python_SANS_Script_GUI/SettingsPanel.py (100%) rename TableDataConstructor.py => Python_SANS_Script_GUI/TableDataConstructor.py (100%) rename TableTest.py => Python_SANS_Script_GUI/TableTest.py (100%) rename TableWidget.py => Python_SANS_Script_GUI/TableWidget.py (100%) rename TextFile2.py => Python_SANS_Script_GUI/TextFile2.py (100%) rename app.spec => Python_SANS_Script_GUI/app.spec (100%) delete mode 100644 Transcript_GitCopilot.txt diff --git a/App.py b/Python_SANS_Script_GUI/App.py similarity index 100% rename from App.py rename to Python_SANS_Script_GUI/App.py diff --git a/Dialogs/PreviewDialog.py b/Python_SANS_Script_GUI/Dialogs/PreviewDialog.py similarity index 100% rename from Dialogs/PreviewDialog.py rename to Python_SANS_Script_GUI/Dialogs/PreviewDialog.py diff --git a/EstimationPanel.py b/Python_SANS_Script_GUI/EstimationPanel.py similarity index 100% rename from EstimationPanel.py rename to Python_SANS_Script_GUI/EstimationPanel.py diff --git a/IO/CsvExporter.py b/Python_SANS_Script_GUI/IO/CsvExporter.py similarity index 100% rename from IO/CsvExporter.py rename to Python_SANS_Script_GUI/IO/CsvExporter.py diff --git a/IO/ExcelIO.py b/Python_SANS_Script_GUI/IO/ExcelIO.py similarity index 100% rename from IO/ExcelIO.py rename to Python_SANS_Script_GUI/IO/ExcelIO.py diff --git a/MainWindow.py b/Python_SANS_Script_GUI/MainWindow.py similarity index 100% rename from MainWindow.py rename to Python_SANS_Script_GUI/MainWindow.py diff --git a/Models/A2Setting.py b/Python_SANS_Script_GUI/Models/A2Setting.py similarity index 100% rename from Models/A2Setting.py rename to Python_SANS_Script_GUI/Models/A2Setting.py diff --git a/Models/DataCollection.py b/Python_SANS_Script_GUI/Models/DataCollection.py similarity index 100% rename from Models/DataCollection.py rename to Python_SANS_Script_GUI/Models/DataCollection.py diff --git a/Models/Estimation.py b/Python_SANS_Script_GUI/Models/Estimation.py similarity index 100% rename from Models/Estimation.py rename to Python_SANS_Script_GUI/Models/Estimation.py diff --git a/Models/Experiment.py b/Python_SANS_Script_GUI/Models/Experiment.py similarity index 100% rename from Models/Experiment.py rename to Python_SANS_Script_GUI/Models/Experiment.py diff --git a/Models/ExperimentSettings.py b/Python_SANS_Script_GUI/Models/ExperimentSettings.py similarity index 100% rename from Models/ExperimentSettings.py rename to Python_SANS_Script_GUI/Models/ExperimentSettings.py diff --git a/Models/Geometry.py b/Python_SANS_Script_GUI/Models/Geometry.py similarity index 100% rename from Models/Geometry.py rename to Python_SANS_Script_GUI/Models/Geometry.py diff --git a/Models/InstrumentInfo.py b/Python_SANS_Script_GUI/Models/InstrumentInfo.py similarity index 100% rename from Models/InstrumentInfo.py rename to Python_SANS_Script_GUI/Models/InstrumentInfo.py diff --git a/Models/RunOrder.py b/Python_SANS_Script_GUI/Models/RunOrder.py similarity index 100% rename from Models/RunOrder.py rename to Python_SANS_Script_GUI/Models/RunOrder.py diff --git a/Models/WaitForUnits.py b/Python_SANS_Script_GUI/Models/WaitForUnits.py similarity index 100% rename from Models/WaitForUnits.py rename to Python_SANS_Script_GUI/Models/WaitForUnits.py diff --git a/Properties/AssemblyInfo.py b/Python_SANS_Script_GUI/Properties/AssemblyInfo.py similarity index 100% rename from Properties/AssemblyInfo.py rename to Python_SANS_Script_GUI/Properties/AssemblyInfo.py diff --git a/SANS_Icon.ico b/Python_SANS_Script_GUI/SANS_Icon.ico similarity index 100% rename from SANS_Icon.ico rename to Python_SANS_Script_GUI/SANS_Icon.ico diff --git a/Scripting/ScriptGenerator.py b/Python_SANS_Script_GUI/Scripting/ScriptGenerator.py similarity index 100% rename from Scripting/ScriptGenerator.py rename to Python_SANS_Script_GUI/Scripting/ScriptGenerator.py diff --git a/Settings.ini b/Python_SANS_Script_GUI/Settings.ini similarity index 100% rename from Settings.ini rename to Python_SANS_Script_GUI/Settings.ini diff --git a/Settings.py b/Python_SANS_Script_GUI/Settings.py similarity index 100% rename from Settings.py rename to Python_SANS_Script_GUI/Settings.py diff --git a/SettingsPanel.py b/Python_SANS_Script_GUI/SettingsPanel.py similarity index 100% rename from SettingsPanel.py rename to Python_SANS_Script_GUI/SettingsPanel.py diff --git a/TableDataConstructor.py b/Python_SANS_Script_GUI/TableDataConstructor.py similarity index 100% rename from TableDataConstructor.py rename to Python_SANS_Script_GUI/TableDataConstructor.py diff --git a/TableTest.py b/Python_SANS_Script_GUI/TableTest.py similarity index 100% rename from TableTest.py rename to Python_SANS_Script_GUI/TableTest.py diff --git a/TableWidget.py b/Python_SANS_Script_GUI/TableWidget.py similarity index 100% rename from TableWidget.py rename to Python_SANS_Script_GUI/TableWidget.py diff --git a/TextFile2.py b/Python_SANS_Script_GUI/TextFile2.py similarity index 100% rename from TextFile2.py rename to Python_SANS_Script_GUI/TextFile2.py diff --git a/app.spec b/Python_SANS_Script_GUI/app.spec similarity index 100% rename from app.spec rename to Python_SANS_Script_GUI/app.spec diff --git a/Transcript_GitCopilot.txt b/Transcript_GitCopilot.txt deleted file mode 100644 index 76aa41c..0000000 --- a/Transcript_GitCopilot.txt +++ /dev/null @@ -1,3940 +0,0 @@ -to create exe -https://github.com/oop7/Py-to-EXE-Guideau - -dehoni: @workspace /explain - -Workspace: The provided code is a C# file named - -SettingsPanel.xaml.cs - -, which is part of a WPF (Windows Presentation Foundation) application. This file defines the code-behind for a user control named - -SettingsPanel - -. The - -SettingsPanel - - class inherits from - -UserControl - -, which is a base class for defining custom user interface controls in WPF. - -The - -SettingsPanel - - class contains two public events: - -OnPreviewClicked - - and - -OnWriteClicked - -. These events are used to notify subscribers when the corresponding buttons are clicked. Events in C# are a way to provide notifications to other parts of the application when something of interest happens. - -The constructor - -SettingsPanel() - - initializes the component by calling the - -InitializeComponent() - - method. This method is automatically generated and is responsible for loading the XAML-defined UI elements and setting up the control. - -There are two private methods in the class: - -btnPreviewScript_Click - - and - -btnWriteScript_Click - -. These methods are event handlers for button click events. When the - -btnPreviewScript_Click - - method is called (which happens when the associated button is clicked), it checks if there are any subscribers to the - -OnPreviewClicked - - event. If there are, it raises the event by invoking the - -OnPreviewClicked - - delegate, passing the sender and event arguments. - -Similarly, the - -btnWriteScript_Click - - method handles the click event for another button. It checks for subscribers to the - -OnWriteClicked - - event and raises the event if there are any. - -Overall, this code sets up a user control with two buttons, each of which can trigger events that other parts of the application can subscribe to and handle accordingly. This allows for a decoupled design where the logic for handling button clicks can be separated from the user control itself. - -dehoni: Please translate the selected code into python and use approbriate packages. - -GitHub Copilot: Here is the translated code into Python using the `tkinter` package, which is a standard GUI toolkit in Python: - -```python - - -import tkinter as tk -from tkinter import ttk - -class SettingsPanel(ttk.Frame): - def __init__(self, parent): - super().__init__(parent) - self.on_preview_clicked = None - self.on_write_clicked = None - - self.preview_button = ttk.Button(self, text="Preview Script", command=self.btn_preview_script_click) - self.preview_button.pack() - - self.write_button = ttk.Button(self, text="Write Script", command=self.btn_write_script_click) - self.write_button.pack() - - def btn_preview_script_click(self): - if self.on_preview_clicked is not None: - self.on_preview_clicked() - - def btn_write_script_click(self): - if self.on_write_clicked is not None: - self.on_write_clicked() - -if __name__ == "__main__": - root = tk.Tk() - panel = SettingsPanel(root) - panel.pack() - root.mainloop() -``` - -In this Python version: -- The - -SettingsPanel - - class inherits from `ttk.Frame`. -- The `on_preview_clicked` and `on_write_clicked` attributes are used to store callback functions. -- The `btn_preview_script_click` and `btn_write_script_click` methods are defined to call these callbacks if they are set. -- The `ttk.Button` widgets are created for the preview and write actions, and their `command` attributes are set to the corresponding methods. -- The `if __name__ == "__main__":` block creates a simple application to demonstrate the - -SettingsPanel - - usage. - -dehoni: Please translate the selected code into python and use approbriate packages. - -GitHub Copilot: Here is the translated code into Python using the `tkinter` package: - -```python - - -import tkinter as tk -from tkinter import ttk - -class SettingsPanel(ttk.Frame): - def __init__(self, parent): - super().__init__(parent) - - self.settings_vm = SettingsPanelVM() - - self.create_widgets() - - def create_widgets(self): - label = ttk.Label(self, text="Settings", font=("Helvetica", 16, "bold")) - label.pack(pady=10) - - grid_frame = ttk.Frame(self) - grid_frame.pack(padx=10, pady=10) - - # Order - ttk.Label(grid_frame, text="Order:").grid(row=0, column=0, sticky=tk.E) - self.com_order = ttk.Combobox(grid_frame, values=self.settings_vm.order_choices) - self.com_order.grid(row=0, column=1, columnspan=2, padx=5, pady=5) - - # Do SANS - ttk.Label(grid_frame, text="Do SANS:").grid(row=1, column=0, sticky=tk.E) - sans_frame = ttk.Frame(grid_frame) - sans_frame.grid(row=1, column=1, columnspan=2) - self.txt_times_sans = ttk.Entry(sans_frame, width=5) - self.txt_times_sans.pack(side=tk.LEFT, padx=5, pady=5) - ttk.Label(sans_frame, text="time(s)").pack(side=tk.LEFT) - - # Do TRANS - ttk.Label(grid_frame, text="Do TRANS:").grid(row=2, column=0, sticky=tk.E) - trans_frame = ttk.Frame(grid_frame) - trans_frame.grid(row=2, column=1, columnspan=2) - self.txt_times_trans = ttk.Entry(trans_frame, width=5) - self.txt_times_trans.pack(side=tk.LEFT, padx=5, pady=5) - ttk.Label(trans_frame, text="time(s)").pack(side=tk.LEFT) - - # Loop over each run - self.chk_loop_run = ttk.Checkbutton(grid_frame, text="Loop over each run?") - self.chk_loop_run.grid(row=3, column=0, columnspan=3, pady=5) - - # Sample Geometry - ttk.Label(grid_frame, text="Sample Geometry:").grid(row=6, column=0, sticky=tk.E) - self.com_sample_geo = ttk.Combobox(grid_frame, values=self.settings_vm.geometry_choices) - self.com_sample_geo.grid(row=6, column=1, columnspan=2, padx=5, pady=5) - - # Sample Height - ttk.Label(grid_frame, text="Sample Height:").grid(row=7, column=0, sticky=tk.E) - sample_height_frame = ttk.Frame(grid_frame) - sample_height_frame.grid(row=7, column=1, columnspan=2) - self.txt_sample_height = ttk.Entry(sample_height_frame, width=5) - self.txt_sample_height.pack(side=tk.LEFT, padx=5, pady=5) - ttk.Label(sample_height_frame, text="mm").pack(side=tk.LEFT) - - # Sample Width - ttk.Label(grid_frame, text="Sample Width:").grid(row=8, column=0, sticky=tk.E) - sample_width_frame = ttk.Frame(grid_frame) - sample_width_frame.grid(row=8, column=1, columnspan=2) - self.txt_sample_width = ttk.Entry(sample_width_frame, width=5) - self.txt_sample_width.pack(side=tk.LEFT, padx=5, pady=5) - ttk.Label(sample_width_frame, text="mm").pack(side=tk.LEFT) - - # Buttons - button_frame = ttk.Frame(self) - button_frame.pack(pady=10) - self.btn_preview_script = ttk.Button(button_frame, text="Preview Script", command=self.btn_preview_script_click) - self.btn_preview_script.pack(side=tk.LEFT, padx=5) - self.btn_write_script = ttk.Button(button_frame, text="Write Script", command=self.btn_write_script_click) - self.btn_write_script.pack(side=tk.LEFT, padx=5) - - def btn_preview_script_click(self): - if self.on_preview_clicked is not None: - self.on_preview_clicked() - - def btn_write_script_click(self): - if self.on_write_clicked is not None: - self.on_write_clicked() - -class SettingsPanelVM: - def __init__(self): - self.order_choices = ["Choice1", "Choice2", "Choice3"] - self.geometry_choices = ["Geometry1", "Geometry2", "Geometry3"] - -if __name__ == "__main__": - root = tk.Tk() - panel = SettingsPanel(root) - panel.pack() - root.mainloop() -``` - -In this Python version: -- The `SettingsPanel` class inherits from `ttk.Frame`. -- The `SettingsPanelVM` class is a simple ViewModel to provide choices for the comboboxes. -- The `create_widgets` method sets up the UI elements similar to the XAML layout. -- The `btn_preview_script_click` and `btn_write_script_click` methods handle button clicks. -- The `if __name__ == "__main__":` block creates a simple application to demonstrate the `SettingsPanel` usage. - -dehoni: Please translate the selected code into python and use approbriate packages. - -GitHub Copilot: The provided XML file is a project user file for a Visual Studio project. In Python, we can use the `xml.etree.ElementTree` module to create a similar XML structure. Here is the translated code: - -```python - - -import xml.etree.ElementTree as ET - -root = ET.Element("Project", ToolsVersion="14.0", xmlns="http://schemas.microsoft.com/developer/msbuild/2003") - -property_group = ET.SubElement(root, "PropertyGroup") -ET.SubElement(property_group, "PublishUrlHistory").text = "publish\\" -ET.SubElement(property_group, "InstallUrlHistory") -ET.SubElement(property_group, "SupportUrlHistory") -ET.SubElement(property_group, "UpdateUrlHistory") -ET.SubElement(property_group, "BootstrapperUrlHistory") -ET.SubElement(property_group, "ErrorReportUrlHistory") -ET.SubElement(property_group, "FallbackCulture").text = "en-US" -ET.SubElement(property_group, "VerifyUploadedFiles").text = "false" - -tree = ET.ElementTree(root) -tree.write("SANS_Script_GUI_v4.xml", encoding="utf-8", xml_declaration=True) -``` - -This Python script uses the `xml.etree.ElementTree` module to create an XML structure similar to the provided `.csproj.user` file. The resulting XML file will be saved as `SANS_Script_GUI_v4.xml`. - -dehoni: Please translate the selected code into python and use approbriate packages. - -GitHub Copilot: Here is the translated code into Python using the `xml.etree.ElementTree` module: - -```python - - -import xml.etree.ElementTree as ET - -root = ET.Element("Project", ToolsVersion="12.0", DefaultTargets="Build", xmlns="http://schemas.microsoft.com/developer/msbuild/2003") - -ET.SubElement(root, "Import", Project="$(MSBuildExtensionsPath)\\$(MSBuildToolsVersion)\\Microsoft.Common.props", Condition="Exists("$(MSBuildExtensionsPath)\\$(MSBuildToolsVersion)\\Microsoft.Common.props")") - -property_group = ET.SubElement(root, "PropertyGroup") -ET.SubElement(property_group, "Configuration", Condition=" "$(Configuration)" == "" ").text = "Debug" -ET.SubElement(property_group, "Platform", Condition=" "$(Platform)" == "" ").text = "AnyCPU" -ET.SubElement(property_group, "ProjectGuid").text = "{5DBFF2C3-05A0-4B4B-9080-8224D86A7B40}" -ET.SubElement(property_group, "OutputType").text = "WinExe" -ET.SubElement(property_group, "AppDesignerFolder").text = "Properties" -ET.SubElement(property_group, "RootNamespace").text = "LOQ_Script_Gui" -ET.SubElement(property_group, "AssemblyName").text = "SANS_Script_Gui_v4" -ET.SubElement(property_group, "TargetFrameworkVersion").text = "v4.8" -ET.SubElement(property_group, "FileAlignment").text = "512" -ET.SubElement(property_group, "ProjectTypeGuids").text = "{60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}" -ET.SubElement(property_group, "WarningLevel").text = "4" -ET.SubElement(property_group, "IsWebBootstrapper").text = "false" -ET.SubElement(property_group, "TargetFrameworkProfile") -ET.SubElement(property_group, "PublishUrl").text = "publish\\" -ET.SubElement(property_group, "Install").text = "true" -ET.SubElement(property_group, "InstallFrom").text = "Disk" -ET.SubElement(property_group, "UpdateEnabled").text = "false" -ET.SubElement(property_group, "UpdateMode").text = "Foreground" -ET.SubElement(property_group, "UpdateInterval").text = "7" -ET.SubElement(property_group, "UpdateIntervalUnits").text = "Days" -ET.SubElement(property_group, "UpdatePeriodically").text = "false" -ET.SubElement(property_group, "UpdateRequired").text = "false" -ET.SubElement(property_group, "MapFileExtensions").text = "true" -ET.SubElement(property_group, "ApplicationRevision").text = "0" -ET.SubElement(property_group, "ApplicationVersion").text = "1.0.0.%2a" -ET.SubElement(property_group, "UseApplicationTrust").text = "false" -ET.SubElement(property_group, "BootstrapperEnabled").text = "true" - -debug_property_group = ET.SubElement(root, "PropertyGroup", Condition=" "$(Configuration)|$(Platform)" == "Debug|AnyCPU" ") -ET.SubElement(debug_property_group, "PlatformTarget").text = "x64" -ET.SubElement(debug_property_group, "DebugSymbols").text = "true" -ET.SubElement(debug_property_group, "DebugType").text = "full" -ET.SubElement(debug_property_group, "Optimize").text = "false" -ET.SubElement(debug_property_group, "OutputPath").text = "bin\\Debug\\" -ET.SubElement(debug_property_group, "DefineConstants").text = "DEBUG;TRACE" -ET.SubElement(debug_property_group, "ErrorReport").text = "prompt" -ET.SubElement(debug_property_group, "WarningLevel").text = "4" -ET.SubElement(debug_property_group, "Prefer32Bit").text = "false" - -release_property_group = ET.SubElement(root, "PropertyGroup", Condition=" "$(Configuration)|$(Platform)" == "Release|AnyCPU" ") -ET.SubElement(release_property_group, "PlatformTarget").text = "x64" -ET.SubElement(release_property_group, "DebugType").text = "pdbonly" -ET.SubElement(release_property_group, "Optimize").text = "true" -ET.SubElement(release_property_group, "OutputPath").text = "bin\\Release\\" -ET.SubElement(release_property_group, "DefineConstants").text = "TRACE" -ET.SubElement(release_property_group, "ErrorReport").text = "prompt" -ET.SubElement(release_property_group, "WarningLevel").text = "4" - -icon_property_group = ET.SubElement(root, "PropertyGroup") -ET.SubElement(icon_property_group, "ApplicationIcon").text = "SANS_Icon.ico" - -references = [ - ("Microsoft.Office.Interop.Excel, Version=15.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c, processorArchitecture=MSIL", "True"), - "ReachFramework", - "System", - "System.Data", - "System.Xml", - "Microsoft.CSharp", - "System.Core", - "System.Xml.Linq", - "System.Data.DataSetExtensions", - ("System.Xaml", "4.0"), - "WindowsBase", - "PresentationCore", - "PresentationFramework" -] - -item_group = ET.SubElement(root, "ItemGroup") -for ref in references: - if isinstance(ref, tuple): - reference = ET.SubElement(item_group, "Reference", Include=ref[0]) - ET.SubElement(reference, "EmbedInteropTypes").text = ref[1] - else: - ET.SubElement(item_group, "Reference", Include=ref) - -compile_files = [ - "Models\\DataCollection.cs", - "Models\\InstrumentInfo.cs", - "IO\\CsvExporter.cs", - "Dialogs\\PreviewDialog.xaml.cs", - "IO\\ExcelIO.cs", - "Models\\A2Setting.cs", - "Models\\Estimation.cs", - "Models\\Geometry.cs", - "Models\\RunOrder.cs", - "EstimationPanel.xaml.cs", - "Models\\Experiment.cs", - "Models\\ExperimentSettings.cs", - "Models\\WaitForUnits.cs", - "Scripting\\GeniePythonScriptGenerator.cs", - "Scripting\\IScriptGenerator.cs", - "Scripting\\OpenGenieScriptGenerator.cs", - "SettingsPanel.xaml.cs", - "ViewModels\\DataGridVM.cs", - "ViewModels\\EstimationPanelVM.cs", - "ViewModels\\InstrumentVM.cs", - "ViewModels\\SettingsPanelVM.cs", - "App.xaml.cs", - "MainWindow.xaml.cs", - "Properties\\AssemblyInfo.cs", - "Properties\\Resources.Designer.cs", - "Properties\\Settings.Designer.cs" -] - -for file in compile_files: - compile = ET.SubElement(item_group, "Compile", Include=file) - if file.endswith(".xaml.cs"): - ET.SubElement(compile, "DependentUpon").text = file.replace(".cs", "") - elif file.endswith(".Designer.cs"): - ET.SubElement(compile, "AutoGen").text = "True" - ET.SubElement(compile, "DesignTime").text = "True" - ET.SubElement(compile, "DependentUpon").text = file.replace(".Designer.cs", ".resx") - if "Settings" in file: - ET.SubElement(compile, "DesignTimeSharedInput").text = "True" - -pages = [ - "Dialogs\\PreviewDialog.xaml", - "EstimationPanel.xaml", - "MainWindow.xaml", - "SettingsPanel.xaml" -] - -for page in pages: - page_element = ET.SubElement(item_group, "Page", Include=page) - ET.SubElement(page_element, "SubType").text = "Designer" - ET.SubElement(page_element, "Generator").text = "MSBuild:Compile" - -embedded_resources = [ - "Properties\\Resources.resx" -] - -for resource in embedded_resources: - embedded_resource = ET.SubElement(item_group, "EmbeddedResource", Include=resource) - ET.SubElement(embedded_resource, "Generator").text = "ResXFileCodeGenerator" - ET.SubElement(embedded_resource, "LastGenOutput").text = resource.replace(".resx", ".Designer.cs") - -none_files = [ - "Properties\\Settings.settings", - "App.config" -] - -for none_file in none_files: - none = ET.SubElement(item_group, "None", Include=none_file) - if none_file.endswith(".settings"): - ET.SubElement(none, "Generator").text = "SettingsSingleFileGenerator" - ET.SubElement(none, "LastGenOutput").text = none_file.replace(".settings", ".Designer.cs") - -ET.SubElement(item_group, "AppDesigner", Include="Properties\\") - -bootstrapper_packages = [ - (".NETFramework,Version=v4.5", "Microsoft .NET Framework 4.5 %28x86 and x64%29", "true"), - ("Microsoft.Net.Framework.3.5.SP1", ".NET Framework 3.5 SP1", "false") -] - -for package in bootstrapper_packages: - bootstrapper_package = ET.SubElement(item_group, "BootstrapperPackage", Include=package[0]) - ET.SubElement(bootstrapper_package, "Visible").text = "False" - ET.SubElement(bootstrapper_package, "ProductName").text = package[1] - ET.SubElement(bootstrapper_package, "Install").text = package[2] - -ET.SubElement(item_group, "Resource", Include="SANS_Icon.ico") - -ET.SubElement(item_group, "WCFMetadata", Include="Connected Services\\") - -ET.SubElement(root, "Import", Project="$(MSBuildToolsPath)\\Microsoft.CSharp.targets") - -tree = ET.ElementTree(root) -tree.write("SANS_Script_GUI_v4.xml", encoding="utf-8", xml_declaration=True) -``` - -This Python script uses the `xml.etree.ElementTree` module to create an XML structure similar to the provided `.csproj` file. The resulting XML file will be saved as `SANS_Script_GUI_v4.xml`. - -dehoni: Please translate the selected code into python and use approbriate packages. - -GitHub Copilot: Here is the translated code into Python using the `tkinter` package: - -```python - - -import tkinter as tk -from tkinter import ttk, filedialog, messagebox -from tkinter.scrolledtext import ScrolledText - -class MainWindow(tk.Tk): - def __init__(self): - super().__init__() - - self.title("SANS Script GUI") - self.geometry("800x600") - - self.data = DataGridVM() - self.inst_info = InstrumentInfo() - self.settings = SettingsPanelVM(self.inst_info) - self.estimate = Estimation() - self.instrument = InstrumentVM(self.settings.experiment, self.estimate) - - self.create_widgets() - self.load_last_instrument() - - def create_widgets(self): - self.data_grid = ttk.Treeview(self) - self.data_grid.pack(fill=tk.BOTH, expand=True) - - self.pnl_settings = SettingsPanel(self, self.settings) - self.pnl_settings.pack(fill=tk.X) - self.pnl_settings.on_preview_clicked = self.pnl_settings_on_preview_clicked - self.pnl_settings.on_write_clicked = self.pnl_settings_on_write_clicked - - self.pnl_estimate = EstimationPanel(self, self.estimate) - self.pnl_estimate.pack(fill=tk.X) - self.pnl_estimate.on_calculate_clicked = self.pnl_estimate_on_calculate_clicked - - self.btn_create_new_table = ttk.Button(self, text="Create New Table", command=self.btn_create_new_table_click) - self.btn_create_new_table.pack(side=tk.LEFT, padx=5, pady=5) - - self.btn_open_excel = ttk.Button(self, text="Open Excel", command=self.btn_open_excel_click) - self.btn_open_excel.pack(side=tk.LEFT, padx=5, pady=5) - - self.mnu_main = tk.Menu(self) - self.config(menu=self.mnu_main) - - file_menu = tk.Menu(self.mnu_main, tearoff=0) - self.mnu_main.add_cascade(label="File", menu=file_menu) - file_menu.add_command(label="Export to Excel", command=self.mnu_export_to_excel_click) - file_menu.add_command(label="Save as CSV", command=self.mnu_save_csv_click) - file_menu.add_separator() - file_menu.add_command(label="Exit", command=self.mnu_exit_click) - - def load_last_instrument(self): - inst = "LARMOR" # Replace with actual settings retrieval - if inst == "LARMOR": - self.instrument.is_larmor = True - elif inst == "LOQ": - self.instrument.is_loq = True - elif inst == "SANS2D": - self.instrument.is_sans2d = True - elif inst == "ZOOM": - self.instrument.is_zoom = True - else: - self.instrument.is_larmor = True - - def pnl_settings_on_write_clicked(self): - filetypes = [("genie_python Files", "*.py")] if self.instrument.is_larmor or self.instrument.is_loq or self.instrument.is_zoom or self.instrument.is_sans2d else [("Open Genie Files", "*.gcl")] - filepath = filedialog.asksaveasfilename(defaultextension=filetypes[0][1], filetypes=filetypes) - if filepath: - scriptname = filepath.split("/")[-1].replace(" ", "_").rsplit(".", 1)[0] - script = self.inst_info.script_generator.generate_script(self.data.runs, self.settings.experiment, scriptname) - with open(filepath, "w") as file: - file.write(script) - - def pnl_settings_on_preview_clicked(self): - script = self.inst_info.script_generator.generate_script(self.data.runs, self.settings.experiment, "my_script") - preview_dialog = PreviewDialog(self, script) - preview_dialog.show() - - def pnl_estimate_on_calculate_clicked(self): - self.estimate.script_time = self.inst_info.script_generator.estimate_run_time(self.data.runs, self.settings.experiment, self.estimate) - - def btn_create_new_table_click(self): - self.data.runs.clear() - - def btn_open_excel_click(self): - filepath = filedialog.askopenfilename(defaultextension=".xlsx", filetypes=[("Excel Files", "*.xls;*.xlsx")]) - if filepath: - try: - excel = ExcelIO() - excel.open_excel_workbook(filepath) - exps = excel.extract_experiments() - self.data.runs = exps - except Exception as err: - messagebox.showerror("SANS Script - Error", "Could not open Excel spreadsheet") - - def mnu_exit_click(self): - self.destroy() - - def mnu_export_to_excel_click(self): - excel = ExcelIO() - excel.export_data_to_excel(self.data.runs) - - def mnu_save_csv_click(self): - filepath = filedialog.asksaveasfilename(defaultextension=".csv", filetypes=[("CSV File", "*.csv")]) - if filepath: - try: - CsvExporter.export(filepath, self.data.runs) - except Exception as err: - messagebox.showerror("SANS Script - Error", "Could not save as CSV") - - def on_close(self): - if self.instrument.is_larmor: - last_instrument = "LARMOR" - elif self.instrument.is_loq: - last_instrument = "LOQ" - elif self.instrument.is_sans2d: - last_instrument = "SANS2D" - elif self.instrument.is_zoom: - last_instrument = "ZOOM" - else: - last_instrument = "LARMOR" - # Save last_instrument to settings - self.destroy() - -class SettingsPanel(ttk.Frame): - def __init__(self, parent, settings_vm): - super().__init__(parent) - self.settings_vm = settings_vm - self.on_preview_clicked = None - self.on_write_clicked = None - self.create_widgets() - - def create_widgets(self): - self.preview_button = ttk.Button(self, text="Preview Script", command=self.preview_click) - self.preview_button.pack(side=tk.LEFT, padx=5, pady=5) - self.write_button = ttk.Button(self, text="Write Script", command=self.write_click) - self.write_button.pack(side=tk.LEFT, padx=5, pady=5) - - def preview_click(self): - if self.on_preview_clicked: - self.on_preview_clicked() - - def write_click(self): - if self.on_write_clicked: - self.on_write_clicked() - -class EstimationPanel(ttk.Frame): - def __init__(self, parent, estimate): - super().__init__(parent) - self.estimate = estimate - self.on_calculate_clicked = None - self.create_widgets() - - def create_widgets(self): - self.calculate_button = ttk.Button(self, text="Calculate", command=self.calculate_click) - self.calculate_button.pack(side=tk.LEFT, padx=5, pady=5) - - def calculate_click(self): - if self.on_calculate_clicked: - self.on_calculate_clicked() - -class PreviewDialog(tk.Toplevel): - def __init__(self, parent, script): - super().__init__(parent) - self.title("Preview Script") - self.geometry("600x400") - self.script_text = ScrolledText(self) - self.script_text.pack(fill=tk.BOTH, expand=True) - self.script_text.insert(tk.END, script) - self.script_text.config(state=tk.DISABLED) - - def show(self): - self.grab_set() - self.wait_window() - -class DataGridVM: - def __init__(self): - self.runs = [] - -class SettingsPanelVM: - def __init__(self, inst_info): - self.experiment = Experiment() - self.inst_info = inst_info - -class Estimation: - def __init__(self): - self.script_time = 0 - -class InstrumentInfo: - class ScriptGenerator: - @staticmethod - def generate_script(runs, experiment, scriptname): - return f"# Script for {scriptname}\n# Runs: {runs}\n# Experiment: {experiment}" - - @staticmethod - def estimate_run_time(runs, experiment, estimate): - return len(runs) * 10 # Dummy implementation - -class InstrumentVM: - def __init__(self, experiment, estimate): - self.is_larmor = False - self.is_loq = False - self.is_sans2d = False - self.is_zoom = False - self.experiment = experiment - self.estimate = estimate - -class ExcelIO: - def open_excel_workbook(self, filename): - pass # Dummy implementation - - def extract_experiments(self): - return [] # Dummy implementation - - def export_data_to_excel(self, runs): - pass # Dummy implementation - -class CsvExporter: - @staticmethod - def export(filename, runs): - pass # Dummy implementation - -class Experiment: - pass # Dummy implementation - -if __name__ == "__main__": - app = MainWindow() - app.protocol("WM_DELETE_WINDOW", app.on_close) - app.mainloop() -``` - -In this Python version: -- The - -MainWindow - - class inherits from `tk.Tk` and sets up the main window and its widgets. -- The `SettingsPanel` and `EstimationPanel` classes are custom frames for the settings and estimation panels. -- The - -PreviewDialog - - class is a custom dialog for previewing the script. -- The - -DataGridVM - -, - -SettingsPanelVM - -, - -Estimation - -, - -InstrumentInfo - -, - -InstrumentVM - -, - -ExcelIO - -, - -CsvExporter - -, and - -Experiment - - classes are placeholders for the corresponding functionality. -- The `if __name__ == "__main__":` block creates and runs the main application window. - -dehoni: Please translate the selected code into python and use approbriate packages. - -GitHub Copilot: Here is the translated code into Python using the `tkinter` package: - -```python - - -import tkinter as tk -from tkinter import ttk, Menu, filedialog, messagebox - -class MainWindow(tk.Tk): - def __init__(self): - super().__init__() - - self.title("SANS Script") - self.geometry("1400x650") - - self.create_widgets() - - def create_widgets(self): - self.grid_columnconfigure(1, weight=1) - self.grid_rowconfigure(4, weight=1) - - # Menu - self.mnu_main = Menu(self) - self.config(menu=self.mnu_main) - - file_menu = Menu(self.mnu_main, tearoff=0) - self.mnu_main.add_cascade(label="_File", menu=file_menu) - file_menu.add_command(label="Export Table to Excel", command=self.mnu_export_to_excel_click) - file_menu.add_command(label="Save as CSV", command=self.mnu_save_csv_click) - file_menu.add_separator() - file_menu.add_command(label="E_xit", command=self.mnu_exit_click) - - instrument_menu = Menu(self.mnu_main, tearoff=0) - self.mnu_main.add_cascade(label="_Instrument", menu=instrument_menu) - self.is_larmor = tk.BooleanVar() - self.is_loq = tk.BooleanVar() - self.is_sans2d = tk.BooleanVar() - self.is_zoom = tk.BooleanVar() - instrument_menu.add_checkbutton(label="LARMOR", variable=self.is_larmor) - instrument_menu.add_checkbutton(label="LOQ", variable=self.is_loq) - instrument_menu.add_checkbutton(label="SANS2D", variable=self.is_sans2d) - instrument_menu.add_checkbutton(label="ZOOM", variable=self.is_zoom) - - # Buttons - button_frame = ttk.Frame(self) - button_frame.grid(row=1, column=0, columnspan=3, pady=10) - self.btn_open_excel = ttk.Button(button_frame, text="Load Excel File", command=self.btn_open_excel_click) - self.btn_open_excel.pack(side=tk.LEFT, padx=5) - self.btn_create_new_table = ttk.Button(button_frame, text="Create New Table", command=self.btn_create_new_table_click) - self.btn_create_new_table.pack(side=tk.LEFT, padx=5) - - # Settings Panel - self.pnl_settings = SettingsPanel(self) - self.pnl_settings.grid(row=2, column=0, columnspan=3, sticky="ew") - - # Estimation Panel - self.pnl_estimate = EstimationPanel(self) - self.pnl_estimate.grid(row=3, column=0, columnspan=3, sticky="ew") - - # Data Grid - self.data_grid = ttk.Treeview(self, columns=("POSITION", "TRANS", "TRANS__WAIT", "SANS", "SANS__WAIT", "PERIOD", "SAMPLE__IDENTIFICATION__STRING", "BACKGROUND", "THICKNESS", "TEMPERATURE", "TEMPERATURE2", "FIELD", "SHEAR__RATE__1", "SHEAR__RATE__2", "SHEAR__ANGLE__1", "SHEAR__ANGLE__2", "PRE__COMMAND", "POST__COMMAND", "RB__NUMBER"), show="headings") - self.data_grid.grid(row=1, column=1, rowspan=4, columnspan=2, sticky="nsew", padx=5, pady=5) - for col in self.data_grid["columns"]: - self.data_grid.heading(col, text=col.replace("__", " ")) - - def btn_open_excel_click(self): - filepath = filedialog.askopenfilename(defaultextension=".xlsx", filetypes=[("Excel Files", "*.xls;*.xlsx")]) - if filepath: - try: - excel = ExcelIO() - excel.open_excel_workbook(filepath) - exps = excel.extract_experiments() - self.data_grid.delete(*self.data_grid.get_children()) - for exp in exps: - self.data_grid.insert("", "end", values=exp) - except Exception as err: - messagebox.showerror("SANS Script - Error", "Could not open Excel spreadsheet") - - def btn_create_new_table_click(self): - self.data_grid.delete(*self.data_grid.get_children()) - - def mnu_export_to_excel_click(self): - excel = ExcelIO() - excel.export_data_to_excel(self.data_grid.get_children()) - - def mnu_save_csv_click(self): - filepath = filedialog.asksaveasfilename(defaultextension=".csv", filetypes=[("CSV File", "*.csv")]) - if filepath: - try: - CsvExporter.export(filepath, self.data_grid.get_children()) - except Exception as err: - messagebox.showerror("SANS Script - Error", "Could not save as CSV") - - def mnu_exit_click(self): - self.destroy() - -class SettingsPanel(ttk.Frame): - def __init__(self, parent): - super().__init__(parent) - self.create_widgets() - - def create_widgets(self): - self.preview_button = ttk.Button(self, text="Preview Script", command=self.preview_click) - self.preview_button.pack(side=tk.LEFT, padx=5, pady=5) - self.write_button = ttk.Button(self, text="Write Script", command=self.write_click) - self.write_button.pack(side=tk.LEFT, padx=5, pady=5) - - def preview_click(self): - pass # Implement preview click functionality - - def write_click(self): - pass # Implement write click functionality - -class EstimationPanel(ttk.Frame): - def __init__(self, parent): - super().__init__(parent) - self.create_widgets() - - def create_widgets(self): - self.calculate_button = ttk.Button(self, text="Calculate", command=self.calculate_click) - self.calculate_button.pack(side=tk.LEFT, padx=5, pady=5) - - def calculate_click(self): - pass # Implement calculate click functionality - -class ExcelIO: - def open_excel_workbook(self, filename): - pass # Dummy implementation - - def extract_experiments(self): - return [] # Dummy implementation - - def export_data_to_excel(self, runs): - pass # Dummy implementation - -class CsvExporter: - @staticmethod - def export(filename, runs): - pass # Dummy implementation - -if __name__ == "__main__": - app = MainWindow() - app.mainloop() -``` - -In this Python version: -- The `MainWindow` class inherits from `tk.Tk` and sets up the main window and its widgets. -- The `SettingsPanel` and `EstimationPanel` classes are custom frames for the settings and estimation panels. -- The `ExcelIO` and `CsvExporter` classes are placeholders for the corresponding functionality. -- The `if __name__ == "__main__":` block creates and runs the main application window. - -dehoni: Please translate the selected code into python and use approbriate packages. - -GitHub Copilot: Here is the translated code into Python using the `tkinter` package: - -```python - - -import tkinter as tk -from tkinter import ttk - -class EstimationPanel(ttk.Frame): - def __init__(self, parent): - super().__init__(parent) - self.on_calculate_clicked = None - self.create_widgets() - - def create_widgets(self): - self.calculate_button = ttk.Button(self, text="Calculate", command=self.btn_calculate_click) - self.calculate_button.pack(padx=5, pady=5) - - def btn_calculate_click(self): - if self.on_calculate_clicked is not None: - self.on_calculate_clicked() - -if __name__ == "__main__": - root = tk.Tk() - panel = EstimationPanel(root) - panel.pack() - root.mainloop() -``` - -In this Python version: -- The - -EstimationPanel - - class inherits from `ttk.Frame`. -- The `on_calculate_clicked` attribute is used to store a callback function. -- The `create_widgets` method sets up the UI elements. -- The `btn_calculate_click` method handles the button click event and calls the callback if it is set. -- The `if __name__ == "__main__":` block creates a simple application to demonstrate the - -EstimationPanel - - usage. - -dehoni: Please translate the selected code into python and use approbriate packages. - -GitHub Copilot: Here is the translated code into Python using the `tkinter` package: - -```python - - -import tkinter as tk -from tkinter import ttk - -class EstimationPanel(ttk.Frame): - def __init__(self, parent, view_model): - super().__init__(parent) - self.view_model = view_model - self.on_calculate_clicked = None - self.create_widgets() - - def create_widgets(self): - self.pack(fill=tk.BOTH, expand=True) - - label = ttk.Label(self, text="Estimated Script Time", font=("Helvetica", 16, "bold")) - label.pack(pady=10) - - grid_frame = ttk.Frame(self) - grid_frame.pack(padx=10, pady=10) - - # Est. count rate - ttk.Label(grid_frame, text="Est. count rate:").grid(row=0, column=0, sticky=tk.E) - count_rate_frame = ttk.Frame(grid_frame) - count_rate_frame.grid(row=0, column=1, columnspan=2) - self.txt_count_rate = ttk.Entry(count_rate_frame, width=10, textvariable=self.view_model.count_rate) - self.txt_count_rate.pack(side=tk.LEFT, padx=5, pady=5) - ttk.Label(count_rate_frame, text="µA/hr").pack(side=tk.LEFT) - - # Est. time between moves - ttk.Label(grid_frame, text="Est. time between moves:").grid(row=1, column=0, sticky=tk.E) - move_time_frame = ttk.Frame(grid_frame) - move_time_frame.grid(row=1, column=1, columnspan=2) - self.txt_move_time = ttk.Entry(move_time_frame, width=10, textvariable=self.view_model.move_time) - self.txt_move_time.pack(side=tk.LEFT, padx=5, pady=5) - ttk.Label(move_time_frame, text="seconds").pack(side=tk.LEFT) - - # Est. script time - ttk.Label(grid_frame, text="Est. script time:").grid(row=2, column=0, sticky=tk.E) - script_time_frame = ttk.Frame(grid_frame) - script_time_frame.grid(row=2, column=1, columnspan=2) - self.txt_estimate = ttk.Entry(script_time_frame, width=15, textvariable=self.view_model.script_time, state="readonly") - self.txt_estimate.pack(side=tk.LEFT, padx=5, pady=5) - - # Calculate button - self.btn_calculate = ttk.Button(grid_frame, text="Calculate", command=self.btn_calculate_click) - self.btn_calculate.grid(row=3, column=1, columnspan=2, pady=10) - - def btn_calculate_click(self): - if self.on_calculate_clicked is not None: - self.on_calculate_clicked() - -class EstimationPanelViewModel: - def __init__(self): - self.count_rate = tk.StringVar() - self.move_time = tk.StringVar() - self.script_time = tk.StringVar() - -if __name__ == "__main__": - root = tk.Tk() - view_model = EstimationPanelViewModel() - panel = EstimationPanel(root, view_model) - panel.pack(fill=tk.BOTH, expand=True) - root.mainloop() -``` - -In this Python version: -- The `EstimationPanel` class inherits from `ttk.Frame`. -- The `EstimationPanelViewModel` class is used to bind data to the UI elements. -- The `create_widgets` method sets up the UI elements similar to the XAML layout. -- The `btn_calculate_click` method handles the button click event and calls the callback if it is set. -- The `if __name__ == "__main__":` block creates a simple application to demonstrate the `EstimationPanel` usage. - -dehoni: Please translate the selected code into python and use approbriate packages. - -GitHub Copilot: Here is the translated code into Python using the `tkinter` package: - -```python - - -import tkinter as tk -from MainWindow import MainWindow - -class App: - def __init__(self): - self.root = tk.Tk() - self.main_window = MainWindow(self.root) - - def run(self): - self.root.mainloop() - -if __name__ == "__main__": - app = App() - app.run() -``` - -In this Python version: -- The - -App - - class initializes the main application window using `tkinter`. -- The `run` method starts the main event loop. -- The `if __name__ == "__main__":` block creates an instance of the - -App - - class and runs the application. - -dehoni: Please translate the selected code into python and use approbriate packages. - -GitHub Copilot: Here is the translated code into Python using the `tkinter` package: - -```python - - -import tkinter as tk -from MainWindow import MainWindow - -class App: - def __init__(self): - self.root = tk.Tk() - self.main_window = MainWindow(self.root) - self.main_window.pack(fill=tk.BOTH, expand=True) - - def run(self): - self.root.mainloop() - -if __name__ == "__main__": - app = App() - app.run() -``` - -In this Python version: -- The `App` class initializes the main application window using `tkinter`. -- The `MainWindow` class is imported and instantiated, and it is packed to fill the main window. -- The `run` method starts the main event loop. -- The `if __name__ == "__main__":` block creates an instance of the `App` class and runs the application. - -dehoni: Please translate the selected code into python and use approbriate packages. - -GitHub Copilot: Here is the translated code into Python using the `configparser` package: - -```python - - -import configparser - -config = configparser.ConfigParser() - -config["DEFAULT"] = { - "LastInstrument": "LARMOR", - "SampleHeight": "11", - "SampleWidth": "11", - "Geometry": "Flat Plate", - "RunOrder": "Alternate - TRANS first", - "NumTrans": "1", - "NumSans": "1", - "LoopPerRun": "False", - "A2SettingSans": "Large = 25, 16 mm", - "A2SettingTrans": "Large = 25, 16 mm", - "CollectionMode": "Events" -} - -with open("app_config.ini", "w") as configfile: - config.write(configfile) -``` - -In this Python version: -- The `configparser` module is used to create and manage the configuration settings. -- The settings from the XML file are added to the `DEFAULT` section of the configuration. -- The configuration is written to a file named `app_config.ini`. - -dehoni: Please translate the selected code into python and use approbriate packages. - -GitHub Copilot: Here is the translated code into Python using the `tkinter` package and the `tkinter` variable classes for data binding: - -```python - - -import tkinter as tk -from tkinter import StringVar -from tkinter import ttk - -class DataGridVM: - def __init__(self): - self._runs = tk.Variable(value=[]) - self.property_changed = [] - - @property - def runs(self): - return self._runs.get() - - @runs.setter - def runs(self, value): - self._runs.set(value) - self.on_property_changed("runs") - - @staticmethod - def wait_for_choices(): - return WaitForUnits.choices() - - def on_property_changed(self, name): - for callback in self.property_changed: - callback(name) - -class WaitForUnits: - @staticmethod - def choices(): - return ["Choice1", "Choice2", "Choice3"] - -class Experiment: - pass # Dummy implementation - -if __name__ == "__main__": - root = tk.Tk() - vm = DataGridVM() - - def on_property_changed(name): - print(f"Property {name} changed") - - vm.property_changed.append(on_property_changed) - - vm.runs = [Experiment(), Experiment()] - print(vm.runs) - - root.mainloop() -``` - -In this Python version: -- The - -DataGridVM - - class uses `tk.Variable` for data binding. -- The - -runs - - property is implemented with getter and setter methods to handle property changes. -- The `on_property_changed` method notifies subscribers when a property changes. -- The - -WaitForUnits - - class provides static choices for the `wait_for_choices` method. -- The - -Experiment - - class is a placeholder for the actual implementation. -- The `if __name__ == "__main__":` block demonstrates how to use the - -DataGridVM - - class and handle property changes. - -dehoni: Please translate the selected code into python and use approbriate packages. - -GitHub Copilot: Here is the translated code into Python using the `tkinter` package and the `tkinter` variable classes for data binding: - -```python - - -import tkinter as tk - -class EstimationPanelVM: - def __init__(self): - self.property_changed = [] - - def on_property_changed(self, name): - for callback in self.property_changed: - callback(name) - -if __name__ == "__main__": - root = tk.Tk() - vm = EstimationPanelVM() - - def on_property_changed(name): - print(f"Property {name} changed") - - vm.property_changed.append(on_property_changed) - - # Example of triggering property change - vm.on_property_changed("example_property") - - root.mainloop() -``` - -In this Python version: -- The - -EstimationPanelVM - - class uses a list to store property change callbacks. -- The `on_property_changed` method notifies subscribers when a property changes. -- The `if __name__ == "__main__":` block demonstrates how to use the - -EstimationPanelVM - - class and handle property changes. - -dehoni: Please translate the selected code into python and use approbriate packages. - -GitHub Copilot: Here is the translated code into Python using the `tkinter` package and the `tkinter` variable classes for data binding: - -```python - - -import tkinter as tk -from tkinter import StringVar, BooleanVar - -class InstrumentVM: - def __init__(self, settings, estimation): - self.settings = settings - self.estimation = estimation - - self.loq_aperture_sizes = [A2Setting.Large, A2Setting.Medium, A2Setting.Small] - self.sans2d_aperture_sizes = [A2Setting.XLarge, A2Setting.Large, A2Setting.Medium, A2Setting.Small] - self.larmor_aperture_sizes = [A2Setting.Larmor] - self.zoom_aperture_sizes = [A2Setting.Large, A2Setting.Medium, A2Setting.Small] - - self.is_larmor = BooleanVar(value=True) - self.is_loq = BooleanVar(value=False) - self.is_sans2d = BooleanVar(value=False) - self.is_zoom = BooleanVar(value=False) - - self.is_larmor.trace_add("write", self.update_instrument) - self.is_loq.trace_add("write", self.update_instrument) - self.is_sans2d.trace_add("write", self.update_instrument) - self.is_zoom.trace_add("write", self.update_instrument) - - self.update_instrument() - - def update_instrument(self, *args): - if self.is_larmor.get(): - self.set_instrument(True, False, False, False, "A1, S1 Setting:", self.larmor_aperture_sizes, 40) - elif self.is_loq.get(): - self.set_instrument(False, True, False, False, "A2 Setting:", self.loq_aperture_sizes, 170) - elif self.is_sans2d.get(): - self.set_instrument(False, False, True, False, "A6 Setting:", self.sans2d_aperture_sizes, 40) - elif self.is_zoom.get(): - self.set_instrument(False, False, False, True, "A6 Setting:", self.zoom_aperture_sizes, 40) - - def set_instrument(self, larmor, loq, sans2d, zoom, a2_label, a2_choices, count_rate): - self.is_larmor.set(larmor) - self.is_loq.set(loq) - self.is_sans2d.set(sans2d) - self.is_zoom.set(zoom) - - InstrumentInfo.script_generator = Scripting.GeniePythonScriptGenerator(larmor, loq, zoom, sans2d) - InstrumentInfo.a2_label = a2_label - InstrumentInfo.a2_choices = a2_choices - InstrumentInfo.a2_trans_enabled = True - InstrumentInfo.collection_mode_enabled = True - self.estimation.count_rate = count_rate - self.settings.a2_setting_sans = Properties.Settings.Default.a2_setting_sans - self.settings.a2_setting_trans = Properties.Settings.Default.a2_setting_trans - -class A2Setting: - Large = "Large" - Medium = "Medium" - Small = "Small" - XLarge = "XLarge" - Larmor = "Larmor" - -class InstrumentInfo: - script_generator = None - a2_label = "" - a2_choices = [] - a2_trans_enabled = False - collection_mode_enabled = False - -class Scripting: - class GeniePythonScriptGenerator: - def __init__(self, larmor, loq, zoom, sans2d): - pass # Dummy implementation - -class Properties: - class Settings: - class Default: - a2_setting_sans = "Large" - a2_setting_trans = "Large" - -class ExperimentSettings: - def __init__(self): - self.a2_setting_sans = "" - self.a2_setting_trans = "" - -class Estimation: - def __init__(self): - self.count_rate = 0 - -if __name__ == "__main__": - root = tk.Tk() - settings = ExperimentSettings() - estimation = Estimation() - vm = InstrumentVM(settings, estimation) - - def on_property_changed(name): - print(f"Property {name} changed") - - vm.is_larmor.trace_add("write", lambda *args: on_property_changed("is_larmor")) - vm.is_loq.trace_add("write", lambda *args: on_property_changed("is_loq")) - vm.is_sans2d.trace_add("write", lambda *args: on_property_changed("is_sans2d")) - vm.is_zoom.trace_add("write", lambda *args: on_property_changed("is_zoom")) - - root.mainloop() -``` - -In this Python version: -- The - -InstrumentVM - - class uses `BooleanVar` for data binding. -- The `update_instrument` method updates the instrument settings based on the selected instrument. -- The `set_instrument` method sets the instrument-specific properties. -- The - -A2Setting - -, - -InstrumentInfo - -, - -Scripting - -, - -Properties - -, - -ExperimentSettings - -, and - -Estimation - - classes are placeholders for the actual implementation. -- The `if __name__ == "__main__":` block demonstrates how to use the - -InstrumentVM - - class and handle property changes. - -dehoni: Please translate the selected code into python and use approbriate packages. - -GitHub Copilot: Here is the translated code into Python using the `tkinter` package and the `tkinter` variable classes for data binding: - -```python - - -import tkinter as tk -from tkinter import StringVar - -class SettingsPanelVM: - def __init__(self, inst_info=None): - self.property_changed = [] - - self._experiment = ExperimentSettings() - self._inst_info = inst_info if inst_info else InstrumentInfo() - - def on_property_changed(self, name): - for callback in self.property_changed: - callback(name) - - @property - def experiment(self): - return self._experiment - - @experiment.setter - def experiment(self, value): - self._experiment = value - self.on_property_changed("experiment") - - @property - def inst_info(self): - return self._inst_info - - @inst_info.setter - def inst_info(self, value): - self._inst_info = value - self.on_property_changed("inst_info") - - @staticmethod - def order_choices(): - return RunOrder.choices() - - @staticmethod - def geometry_choices(): - return Geometry.choices() - - @staticmethod - def collection_mode_choices(): - return DataCollection.choices() - -class RunOrder: - @staticmethod - def choices(): - return ["Choice1", "Choice2", "Choice3"] - -class Geometry: - @staticmethod - def choices(): - return ["Geometry1", "Geometry2", "Geometry3"] - -class DataCollection: - @staticmethod - def choices(): - return ["Mode1", "Mode2", "Mode3"] - -class ExperimentSettings: - pass # Dummy implementation - -class InstrumentInfo: - pass # Dummy implementation - -if __name__ == "__main__": - root = tk.Tk() - vm = SettingsPanelVM() - - def on_property_changed(name): - print(f"Property {name} changed") - - vm.property_changed.append(on_property_changed) - - vm.experiment = ExperimentSettings() - vm.inst_info = InstrumentInfo() - - root.mainloop() -``` - -In this Python version: -- The - -SettingsPanelVM - - class uses a list to store property change callbacks. -- The `on_property_changed` method notifies subscribers when a property changes. -- The - -experiment - - and `inst_info` properties are implemented with getter and setter methods to handle property changes. -- The `order_choices`, `geometry_choices`, and `collection_mode_choices` methods provide static choices for the combo boxes. -- The - -RunOrder - -, - -Geometry - -, - -DataCollection - -, - -ExperimentSettings - -, and - -InstrumentInfo - - classes are placeholders for the actual implementation. -- The `if __name__ == "__main__":` block demonstrates how to use the - -SettingsPanelVM - - class and handle property changes. - -dehoni: Please translate the selected code into python and use approbriate packages. - -GitHub Copilot: Here is the translated code into Python: - -```python - - -from datetime import datetime, timedelta - -class GeniePythonScriptGenerator: - def __init__(self, is_larmor, is_loq, is_zoom, is_sans2d): - self.indent = " " - self.is_larmor = is_larmor - self.is_loq = is_loq - self.is_zoom = is_zoom - self.is_sans2d = is_sans2d - - def generate_script(self, runs, settings, scriptname): - script = f"# Script created by SANSScript at {datetime.now()}\n\n" - script += self.generate_header(scriptname) - script += self.generate_setup(settings) - script += self.generate_runs(runs, settings) - script += "\n" - return script - - def get_indent_string(self, n): - return self.indent * n - - def generate_header(self, scriptname): - script = "import sys\n" - script += "from genie_python import genie as g, BLOCK_NAMES as b\n" - script += "sys.path.append(r\"c:\\instrument\\scripts\")\n" - - if self.is_larmor: - script += "from instrument.larmor import * # pylint: disable=wildcard-import, unused-wildcard-import\n" - if self.is_loq: - script += "from instrument.loq import * # pylint: disable=wildcard-import, unused-wildcard-import\n" - if self.is_zoom: - script += "from instrument.zoom import * # pylint: disable=wildcard-import, unused-wildcard-import\n" - if self.is_sans2d: - script += "from instrument.sans2d import * # pylint: disable=wildcard-import, unused-wildcard-import\n" - - script += f"\ndef {scriptname}():\n" - return script - - def generate_setup(self, settings): - script = "" - script += self.get_indent_string(1) + f"g.change_sample_par("Width", "{settings.sample_width}")\n" - script += self.get_indent_string(1) + f"g.change_sample_par("height", "{settings.sample_height}")\n" - script += self.get_indent_string(1) + f"g.change_sample_par("Geometry", "{settings.sample_geometry}")\n\n" - return script - - def check_positions(self, runs): - script = "" - if self.is_larmor: - position_list = ",".join([exp.position for exp in runs if exp.position]) - if position_list: - script += self.get_indent_string(1) + f"if check_move_pos(["{position_list}"]) != True:\n" - script += self.get_indent_string(2) + "print("Invalid positions - script aborted")\n" - script += self.get_indent_string(2) + "return\n\n" - return script - - def generate_runs(self, runs, settings): - script = self.check_positions(runs) - if not runs: - return script - - if settings.order == "AllTrans": - script += self.get_indent_string(1) + f"for i in range({settings.num_trans}):\n" - for exp in runs: - if exp.trans: - script = self.do_trans_run(script, exp, 2, settings) - script += self.get_indent_string(1) + f"for i in range({settings.num_sans}):\n" - for exp in runs: - if exp.sans: - script = self.do_sans_run(script, exp, 2, settings) - elif settings.order == "AllSans": - script += self.get_indent_string(1) + f"for i in range({settings.num_sans}):\n" - for exp in runs: - if exp.sans: - script = self.do_sans_run(script, exp, 2, settings) - if settings.num_trans and any(exp.trans for exp in runs): - script += self.get_indent_string(1) + f"for i in range({settings.num_trans}):\n" - for exp in runs: - if exp.trans: - script = self.do_trans_run(script, exp, 2, settings) - else: - script += self.get_indent_string(1) + f"num_sans = {settings.num_sans}\n" - script += self.get_indent_string(1) + f"num_trans = {settings.num_trans}\n" - script += self.get_indent_string(1) + "count = 0\n" - script += self.get_indent_string(1) + "while True:\n" - indent = 2 - for exp in runs: - if settings.order == "TransFirst": - if exp.trans: - script += self.get_indent_string(indent) + "if count < num_trans:\n" - script = self.do_trans_run(script, exp, indent + 1, settings) - if exp.sans: - script += self.get_indent_string(indent) + "if count < num_sans:\n" - script = self.do_sans_run(script, exp, indent + 1, settings) - else: - if exp.sans: - script += self.get_indent_string(indent) + "if count < num_sans:\n" - script = self.do_sans_run(script, exp, indent + 1, settings) - if exp.trans: - script += self.get_indent_string(indent) + "if count < num_trans:\n" - script = self.do_trans_run(script, exp, indent + 1, settings) - script += self.get_indent_string(indent) + "count += 1\n" - script += self.get_indent_string(indent) + "if count >= num_trans and count >= num_sans: break\n" - return script - - def do_trans_run(self, script, exp, indent, settings): - script += self.do_sample_environment(exp, indent) - waitfor = "uamps" - rtype = "histogram" - fulltitle = f"{{{exp.sample}}}" - if settings.collection_mode == "Events": - rtype = "event" - if exp.trans_wait == "Frames": - waitfor = "frames" - elif exp.trans_wait == "Seconds": - waitfor = "seconds" - elif exp.trans_wait == "Minutes": - waitfor = "minutes" - if self.is_larmor or self.is_zoom: - script += self.get_indent_string(indent) + f"do_trans(title="{fulltitle}", pos="{exp.position}", {waitfor}={exp.trans}, thickness={exp.thickness})\n" - elif self.is_loq: - script += self.get_indent_string(indent) + f"do_trans(title="{fulltitle}", pos="{exp.position}", {waitfor}={exp.trans}, thickness={exp.thickness}, aperture="small")\n" - else: - script += self.get_indent_string(indent) + f"do_trans(title="{fulltitle}", pos="{exp.position}", {waitfor}={exp.trans}, thickness={exp.thickness}, dae="transmission", aperture="{self.aperture_string(settings.a2_setting_trans)}")\n" - script += self.get_indent_string(indent) + exp.post_command + "\n" - return script - - def do_sans_run(self, script, exp, indent, settings): - script += self.do_sample_environment(exp, indent) - waitfor = "uamps" - rtype = "histogram" - fulltitle = f"{{{exp.sample}}}" - if exp.background: - fulltitle += f"_{{{exp.background}}}" - if settings.collection_mode in ["Events", "TRANSSANS_Events"]: - rtype = "event" - elif settings.collection_mode == "SESANS": - rtype = "sesans" - if settings.collection_mode in ["TRANSSANS_Events", "TRANSSANS_Histogram"]: - fulltitle += "_TRANS" - if exp.sans_wait == "Frames": - waitfor = "frames" - elif exp.sans_wait == "Seconds": - waitfor = "seconds" - elif exp.sans_wait == "Minutes": - waitfor = "minutes" - if self.is_larmor or self.is_zoom: - script += self.get_indent_string(indent) + f"do_sans(title="{fulltitle}", pos="{exp.position}", {waitfor}={exp.sans}, thickness={exp.thickness}, dae="{rtype}")\n" - else: - script += self.get_indent_string(indent) + f"do_sans(title="{fulltitle}", pos="{exp.position}", {waitfor}={exp.sans}, thickness={exp.thickness}, dae="{rtype}", aperture="{self.aperture_string(settings.a2_setting_sans)}")\n" - script += self.get_indent_string(indent) + exp.post_command + "\n" - return script - - def aperture_string(self, size): - if size in [A2Setting.Large, A2Setting.Larmor]: - return "large" - elif size in [A2Setting.Medium, A2Setting.Larmor]: - return "medium" - elif size in [A2Setting.Small, A2Setting.Larmor]: - return "small" - elif size in [A2Setting.XLarge, A2Setting.Larmor]: - return "xlarge" - return "" - - def do_sample_environment(self, exp, indent): - script = "" - if exp.temperature1: - temp1 = float(exp.temperature1) - temp1low = temp1 - 1.0 - temp1high = temp1 + 1.0 - if self.is_loq: - script += self.get_indent_string(indent) + f"g.cset(internal_setpoint_1={exp.temperature1})\n" - script += self.get_indent_string(indent) + f"g.cset(\"Julabo_1_ext\", lowlimit={temp1low}, highlimit={temp1high}, runcontrol=True)\n" - else: - script += self.get_indent_string(indent) + f"g.cset(JULABO1_SP={exp.temperature1})\n" - script += self.get_indent_string(indent) + f"g.cset(\"JULABO1_TEMP\", lowlimit={temp1low}, highlimit={temp1high}, runcontrol=True)\n" - if exp.temperature2: - temp2 = float(exp.temperature2) - temp2low = temp2 - 1.0 - temp2high = temp2 + 1.0 - if self.is_loq: - script += self.get_indent_string(indent) + f"g.cset(internal_setpoint_2={exp.temperature2})\n" - script += self.get_indent_string(indent) + f"g.cset(\"Julabo_2_ext\", lowlimit={temp2low}, highlimit={temp2high}, runcontrol=True)\n" - else: - script += self.get_indent_string(indent) + f"g.cset(JULABO2_SP={exp.temperature2})\n" - script += self.get_indent_string(indent) + f"g.cset(\"JULABO2_TEMP\", lowlimit={temp2low}, highlimit={temp2high}, runcontrol=True)\n" - if exp.pre_command: - script += self.get_indent_string(indent) + exp.pre_command + "\n" - if exp.field: - script += self.get_indent_string(indent) + f"g.cset(FIELD={exp.field})\n" - if exp.shear_rate1: - script += self.get_indent_string(indent) + f"g.cset(SHEAR_RATE_1={exp.shear_rate1})\n" - if exp.shear_rate2: - script += self.get_indent_string(indent) + f"g.cset(SHEAR_RATE_2={exp.shear_rate2})\n" - if exp.shear_angle1: - script += self.get_indent_string(indent) + f"g.cset(SHEAR_ANGLE_1={exp.shear_angle1})\n" - if exp.shear_angle2: - script += self.get_indent_string(indent) + f"g.cset(SHEAR_ANGLE_2={exp.shear_angle2})\n" - return script - - def estimate_run_time(self, runs, settings, estimation): - minutes = 0 - if settings.num_sans > 0: - minutes += self.calculate_time(runs, True, estimation.count_rate, estimation.move_time) * settings.num_sans - if settings.num_trans > 0: - minutes += self.calculate_time(runs, False, estimation.count_rate, estimation.move_time) * settings.num_trans - time = datetime(1, 1, 1, 0, 0, 0) + timedelta(minutes=minutes) - return time.strftime("%H:%M:%S") - - @staticmethod - def calculate_time(runs, is_sans, count_rate, seconds_between): - minutes = 0 - for exp in runs: - if exp.position: - if is_sans: - minutes += GeniePythonScriptGenerator.get_minutes(exp.sans_wait, exp.sans, count_rate) - else: - minutes += GeniePythonScriptGenerator.get_minutes(exp.trans_wait, exp.trans, count_rate) - minutes += seconds_between / 60 - return minutes - - @staticmethod - def get_minutes(wait, length, count_rate): - if wait == "Minutes": - return length - elif wait == "Seconds": - return length / 60.0 - elif wait == "Frames": - return length / (25.0 * 60.0) - else: - return length / (count_rate / 60.0) - -class A2Setting: - Large = "Large" - Medium = "Medium" - Small = "Small" - XLarge = "XLarge" - Larmor = "Larmor" - -class Experiment: - def __init__(self, position="", trans=0, sans=0, trans_wait="", sans_wait="", sample="", background="", thickness=0, pre_command="", post_command="", temperature1="", temperature2="", field="", shear_rate1="", shear_rate2="", shear_angle1="", shear_angle2=""): - self.position = position - self.trans = trans - self.sans = sans - self.trans_wait = trans_wait - self.sans_wait = sans_wait - self.sample = sample - self.background = background - self.thickness = thickness - self.pre_command = pre_command - self.post_command = post_command - self.temperature1 = temperature1 - self.temperature2 = temperature2 - self.field = field - self.shear_rate1 = shear_rate1 - self.shear_rate2 = shear_rate2 - self.shear_angle1 = shear_angle1 - self.shear_angle2 = shear_angle2 - -class ExperimentSettings: - def __init__(self, sample_width="", sample_height="", sample_geometry="", order="", num_trans=0, num_sans=0, collection_mode="", a2_setting_trans="", a2_setting_sans=""): - self.sample_width = sample_width - self.sample_height = sample_height - self.sample_geometry = sample_geometry - self.order = order - self.num_trans = num_trans - self.num_sans = num_sans - self.collection_mode = collection_mode - self.a2_setting_trans = a2_setting_trans - self.a2_setting_sans = a2_setting_sans - -class Estimation: - def __init__(self, count_rate=0, move_time=0): - self.count_rate = count_rate - self.move_time = move_time - -if __name__ == "__main__": - runs = [Experiment(position="pos1", trans=10, sans=20, trans_wait="Minutes", sans_wait="Minutes", sample="Sample1", thickness=1.0)] - settings = ExperimentSettings(sample_width="10", sample_height="10", sample_geometry="Flat", order="AllTrans", num_trans=1, num_sans=1, collection_mode="Events", a2_setting_trans="Large", a2_setting_sans="Large") - estimation = Estimation(count_rate=100, move_time=10) - generator = GeniePythonScriptGenerator(is_larmor=True, is_loq=False, is_zoom=False, is_sans2d=False) - script = generator.generate_script(runs, settings, "my_script") - print(script) - estimated_time = generator.estimate_run_time(runs, settings, estimation) - print(f"Estimated run time: {estimated_time}") -``` - -In this Python version: -- The - -GeniePythonScriptGenerator - - class is implemented with methods to generate the script and estimate run time. -- The - -A2Setting - -, - -Experiment - -, - -ExperimentSettings - -, and - -Estimation - - classes are implemented as placeholders for the actual data structures. -- The `if __name__ == "__main__":` block demonstrates how to use the - -GeniePythonScriptGenerator - - class to generate a script and estimate run time. - -dehoni: Please translate the selected code into python and use approbriate packages. - -GitHub Copilot: Here is the translated code into Python using an abstract base class: - -```python - - -from abc import ABC, abstractmethod - -class IScriptGenerator(ABC): - @abstractmethod - def generate_script(self, runs, settings, scriptname): - pass - - @abstractmethod - def estimate_run_time(self, runs, settings, estimation): - pass - -class Experiment: - pass # Dummy implementation - -class ExperimentSettings: - pass # Dummy implementation - -class Estimation: - pass # Dummy implementation -``` - -In this Python version: -- The - -IScriptGenerator - - class is defined as an abstract base class using the `abc` module. -- The `generate_script` and `estimate_run_time` methods are defined as abstract methods. -- The - -Experiment - -, - -ExperimentSettings - -, and - -Estimation - - classes are placeholders for the actual implementations. - -dehoni: Please translate the selected code into python and use approbriate packages. - -GitHub Copilot: Here is the translated code into Python: - -```python - - -from datetime import datetime, timedelta - -class OpenGenieScriptGenerator: - def __init__(self, is_sans2d): - self.is_sans2d = is_sans2d - - def generate_script(self, runs, settings, scriptname): - script = f"# Script created by LOQ Script at {datetime.now()}\n" - script += "# Script MUST be loaded in to Open GENIE using the loadscript command.\n\n" - - if self.is_sans2d: - script += "QUALIFIERS /test\n" - - script += f"LOCAL transCount = 0 sansCount = 0 numTRANS={settings.num_trans} numSANS={settings.num_sans}\n\n" - script += "SETSCRIPTNAME THIS_PROCEDURE()\n\n" - script += f"Sample_par width={settings.sample_width} height={settings.sample_height} geometry="{settings.sample_geometry}"\n\n" - - if self.is_sans2d: - position_list = ",".join([exp.position for exp in runs if exp.position]) - if position_list: - script += f"IF NOT CHECK_MOVE_POS("{position_list}")\n" - script += " PRINTEN "Invalid positions - script aborted"\n" - script += " RETURN\n" - script += "ENDIF\n\n" - script += "IF test\n" - script += " RETURN\n" - script += "ENDIF\n\n" - - if settings.order == "AllTrans": - if settings.loop_per_run: - script += self.do_all_trans_loop_each(runs, settings, True, True) - script += self.do_all_sans_loop_each(runs, settings, True, True) - else: - script += "LOOP\n\n" - script += self.do_all_trans(runs, settings, True, True) - script += self.do_all_sans(runs, settings, True, True) - script += "EXITIF ((transCount >= numTRANS) AND (sansCount >= numSANS))\n\n" - script += "ENDLOOP\n\n" - elif settings.order == "TransFirst": - if settings.loop_per_run: - for exp in runs: - script += self.do_single_trans_loop_each(exp, settings, True, False) - script += self.do_single_sans_loop_each(exp, settings, False, True) - else: - script += "LOOP\n\n" - for exp in runs: - script += self.do_single_trans(exp, settings, True, False) - script += self.do_single_sans(exp, settings, False, True) - script += "transCount = transCount + 1\n" - script += "sansCount = sansCount + 1\n\n" - script += "EXITIF ((transCount >= numTRANS) AND (sansCount >= numSANS))\n\n" - script += "ENDLOOP\n\n" - elif settings.order == "AllSans": - if settings.loop_per_run: - script += self.do_all_sans_loop_each(runs, settings, True, True) - script += self.do_all_trans_loop_each(runs, settings, True, True) - else: - script += "LOOP\n\n" - script += self.do_all_sans(runs, settings, True, True) - script += self.do_all_trans(runs, settings, True, True) - script += "EXITIF ((transCount >= numTRANS) AND (sansCount >= numSANS))\n\n" - script += "ENDLOOP\n\n" - elif settings.order == "SansFirst": - if settings.loop_per_run: - for exp in runs: - script += self.do_single_sans_loop_each(exp, settings, True, False) - script += self.do_single_trans_loop_each(exp, settings, False, True) - else: - script += "LOOP\n\n" - for exp in runs: - script += self.do_single_sans(exp, settings, True, False) - script += self.do_single_trans(exp, settings, False, True) - script += "transCount = transCount + 1\n" - script += "sansCount = sansCount + 1\n\n" - script += "EXITIF ((transCount >= numTRANS) AND (sansCount >= numSANS))\n\n" - script += "ENDLOOP\n\n" - - script += "PRINTN "SCRIPT COMPLETED "\n\n" - return script - - def do_single_sans(self, exp, settings, do_pre_com, do_post_com): - script = "" - if settings.num_sans > 0 and exp.sans > 0: - script += "IF (sansCount < numSANS)\n" - script += self.set_sans_aperture(settings.a2_setting_sans) - script += self.do_single_run(False, exp, False, do_pre_com, do_post_com, settings) - script += "\nENDIF\n\n" - return script - - def do_single_trans(self, exp, settings, do_pre_com, do_post_com): - script = "" - if settings.num_trans > 0 and exp.trans > 0: - script += "IF (transCount < numTRANS)\n" - script += self.set_trans_aperture(settings.a2_setting_trans) - script += self.do_single_run(True, exp, False, do_pre_com, do_post_com, settings) - script += "\nENDIF\n\n" - return script - - def do_single_sans_loop_each(self, exp, settings, do_pre_com, do_post_com): - script = "" - if settings.num_sans > 0 and exp.sans > 0: - script += self.set_sans_aperture(settings.a2_setting_sans) - script += self.do_single_run(False, exp, True, do_pre_com, do_post_com, settings) + "\n" - return script - - def do_single_trans_loop_each(self, exp, settings, do_pre_com, do_post_com): - script = "" - if settings.num_trans > 0 and exp.trans > 0: - script += self.set_trans_aperture(settings.a2_setting_trans) - script += self.do_single_run(True, exp, True, do_pre_com, do_post_com, settings) + "\n" - return script - - def do_all_sans(self, runs, settings, do_pre_com, do_post_com): - script = "" - if settings.num_sans > 0: - script += self.set_sans_aperture(settings.a2_setting_sans) - script += "IF sansCount < numSANS\n\n" - for exp in runs: - script += self.do_single_run(False, exp, False, do_pre_com, do_post_com, settings) + "\n" - script += "sansCount = sansCount + 1\n\n" - script += "ENDIF\n\n" - return script - - def do_all_trans(self, runs, settings, do_pre_com, do_post_com): - script = "" - if settings.num_trans > 0: - script += self.set_trans_aperture(settings.a2_setting_trans) - script += "IF transCount < numTRANS\n\n" - for exp in runs: - script += self.do_single_run(True, exp, False, do_pre_com, do_post_com, settings) + "\n" - script += "transCount = transCount + 1\n\n" - script += "ENDIF\n\n" - return script - - def do_all_sans_loop_each(self, runs, settings, do_pre_com, do_post_com): - script = "" - if settings.num_sans > 0: - script += self.set_sans_aperture(settings.a2_setting_sans) - for exp in runs: - script += self.do_single_run(False, exp, True, do_pre_com, do_post_com, settings) + "\n" - return script - - def do_all_trans_loop_each(self, runs, settings, do_pre_com, do_post_com): - script = "" - if settings.num_trans > 0: - script += self.set_trans_aperture(settings.a2_setting_trans) - for exp in runs: - script += self.do_single_run(True, exp, True, do_pre_com, do_post_com, settings) + "\n" - return script - - def set_sans_aperture(self, a2_size_sans): - script = "" - if a2_size_sans == "Large": - script += "DO_SANS /LARGE\n\n" - elif a2_size_sans == "Medium": - script += "DO_SANS /MEDIUM\n\n" - elif a2_size_sans == "Small": - script += "DO_SANS /SMALL\n\n" - elif a2_size_sans == "Trans": - script += "DO_SANS /TRANS\n\n" - return script - - def set_trans_aperture(self, a2_size_trans): - script = "" - if not a2_size_trans: - script += "DO_TRANS\n\n" - else: - if a2_size_trans == "Large": - script += "DO_TRANS /LARGE\n\n" - elif a2_size_trans == "Medium": - script += "DO_TRANS /MEDIUM\n\n" - elif a2_size_trans == "Small": - script += "DO_TRANS /SMALL\n\n" - elif a2_size_trans == "Trans": - script += "DO_TRANS /TRANS\n\n" - return script - - def do_single_run(self, is_trans, exp, is_loop, do_pre_com, do_post_com, settings): - script = "" - if exp.trans > 0 or exp.sans > 0: - if exp.rb_number: - script += f"change rbno={exp.rb_number}\n" - if exp.position: - if do_pre_com: - script += self.do_sample_environment(exp) - if is_loop: - if is_trans: - script += "LOOP transCount FROM 1 TO numTRANS\n" - else: - script += "LOOP sansCount FROM 1 TO numSANS\n" - script += self.create_move_command(is_trans, exp, settings) - script += "ENDLOOP\n" - else: - script += self.create_move_command(is_trans, exp, settings) - if do_post_com and exp.post_command: - script += exp.post_command + "\n" - return script - - def do_sample_environment(self, exp): - script = "" - if exp.temperature1: - script += f"CSET TEMP={exp.temperature1}\n" - if exp.temperature2: - script += f"CSET TEMP2={exp.temperature2}\n" - if exp.pre_command: - script += exp.pre_command + "\n" - if exp.field: - script += f"CSET FIELD={exp.field}\n" - if exp.shear_rate1: - script += f"CSET SHEAR_RATE_1={exp.shear_rate1}\n" - if exp.shear_rate2: - script += f"CSET SHEAR_RATE_2={exp.shear_rate2}\n" - if exp.shear_angle1: - script += f"CSET SHEAR_ANGLE_1={exp.shear_angle1}\n" - if exp.shear_angle2: - script += f"CSET SHEAR_ANGLE_2={exp.shear_angle2}\n" - return script - - def create_move_command(self, is_trans, exp, settings): - script = f"MOVE pos="{exp.position}"" - fulltitle = f"{{{exp.sample}}}" - if exp.background: - fulltitle += f"_{{{exp.background}}}" - if exp.thickness: - script += f" thick={exp.thickness}" - else: - script += " thick=1" - if exp.period: - script += f" period={exp.period}" - if is_trans: - script += self.write_wait_for(exp.trans_wait, exp.trans) - if fulltitle: - script += f" title="{fulltitle}_TRANS"" - else: - script += self.write_wait_for(exp.sans_wait, exp.sans) - if fulltitle: - if settings.collection_mode in ["TRANSSANS_Events", "TRANSSANS_Histogram"]: - script += f" title="{fulltitle}_TRANS_SANS"" - else: - script += f" title="{fulltitle}_SANS"" - return script + "\n" - - def write_wait_for(self, waitfor, length): - script = "" - if waitfor: - if waitfor.lower() == "MicroAmps".lower(): - script += f" uAhr={length}" - elif waitfor.lower() == "Minutes".lower(): - script += f" min={length}" - elif waitfor.lower() == "Seconds".lower(): - script += f" seconds={length}" - elif waitfor.lower() == "Frames".lower(): - script += f" frame={length}" - else: - script += f" uAhr={length}" - else: - script += f" uAhr={length}" - return script - - def estimate_run_time(self, runs, settings, estimation): - minutes = 0 - if settings.num_sans > 0: - minutes += self.calculate_time(runs, True, estimation.count_rate, estimation.move_time) * settings.num_sans - if settings.num_trans > 0: - minutes += self.calculate_time(runs, False, estimation.count_rate, estimation.move_time) * settings.num_trans - time = datetime(1, 1, 1, 0, 0, 0) + timedelta(minutes=minutes) - return time.strftime("%H:%M:%S") - - @staticmethod - def calculate_time(runs, is_sans, count_rate, seconds_between): - minutes = 0 - for exp in runs: - if exp.position: - if is_sans: - minutes += OpenGenieScriptGenerator.get_minutes(exp.sans_wait, exp.sans, count_rate) - else: - minutes += OpenGenieScriptGenerator.get_minutes(exp.trans_wait, exp.trans, count_rate) - minutes += seconds_between / 60 - return minutes - - @staticmethod - def get_minutes(wait, length, count_rate): - if wait == "Minutes": - return length - elif wait == "Seconds": - return length / 60.0 - elif wait == "Frames": - return length / (25.0 * 60.0) - else: - return length / (count_rate / 60.0) - -class Experiment: - def __init__(self, position="", trans=0, sans=0, trans_wait="", sans_wait="", sample="", background="", thickness=0, pre_command="", post_command="", temperature1="", temperature2="", field="", shear_rate1="", shear_rate2="", shear_angle1="", shear_angle2="", rb_number="", period=""): - self.position = position - self.trans = trans - self.sans = sans - self.trans_wait = trans_wait - self.sans_wait = sans_wait - self.sample = sample - self.background = background - self.thickness = thickness - self.pre_command = pre_command - self.post_command = post_command - self.temperature1 = temperature1 - self.temperature2 = temperature2 - self.field = field - self.shear_rate1 = shear_rate1 - self.shear_rate2 = shear_rate2 - self.shear_angle1 = shear_angle1 - self.shear_angle2 = shear_angle2 - self.rb_number = rb_number - self.period = period - -class ExperimentSettings: - def __init__(self, sample_width="", sample_height="", sample_geometry="", order="", num_trans=0, num_sans=0, collection_mode="", a2_setting_trans="", a2_setting_sans="", loop_per_run=False): - self.sample_width = sample_width - self.sample_height = sample_height - self.sample_geometry = sample_geometry - self.order = order - self.num_trans = num_trans - self.num_sans = num_sans - self.collection_mode = collection_mode - self.a2_setting_trans = a2_setting_trans - self.a2_setting_sans = a2_setting_sans - self.loop_per_run = loop_per_run - -class Estimation: - def __init__(self, count_rate=0, move_time=0): - self.count_rate = count_rate - self.move_time = move_time - -if __name__ == "__main__": - runs = [Experiment(position="pos1", trans=10, sans=20, trans_wait="Minutes", sans_wait="Minutes", sample="Sample1", thickness=1.0)] - settings = ExperimentSettings(sample_width="10", sample_height="10", sample_geometry="Flat", order="AllTrans", num_trans=1, num_sans=1, collection_mode="Events", a2_setting_trans="Large", a2_setting_sans="Large", loop_per_run=False) - estimation = Estimation(count_rate=100, move_time=10) - generator = OpenGenieScriptGenerator(is_sans2d=True) - script = generator.generate_script(runs, settings, "my_script") - print(script) - estimated_time = generator.estimate_run_time(runs, settings, estimation) - print(f"Estimated run time: {estimated_time}") -``` - -In this Python version: -- The `OpenGenieScriptGenerator` class is implemented with methods to generate the script and estimate run time. -- The `Experiment`, `ExperimentSettings`, and `Estimation` classes are implemented as placeholders for the actual data structures. -- The `if __name__ == "__main__":` block demonstrates how to use the `OpenGenieScriptGenerator` class to generate a script and estimate run time. - -dehoni: Please translate the selected code into python and use approbriate packages. - -GitHub Copilot: Here is the translated code into Python using the `setuptools` package for setting up package metadata: - -```python - - -from setuptools import setup, find_packages - -setup( - name="LOQ_Script_Gui", - version="1.0.0", - description="", - author="", - author_email="", - url="", - packages=find_packages(), - include_package_data=True, - install_requires=[ - # Add your dependencies here - ], - classifiers=[ - "Programming Language :: Python :: 3", - "License :: OSI Approved :: MIT License", - "Operating System :: OS Independent", - ], - python_requires=">=3.6", -) - -# Additional metadata for localization and resource management can be added as needed. -``` - -In this Python version: -- The `setup.py` file is used to define the package metadata and dependencies. -- The `setuptools` package is used to handle the package setup. -- The `find_packages` function is used to automatically discover all packages and subpackages. -- The `install_requires` list can be populated with the required dependencies. -- The `classifiers` list is used to provide additional metadata about the package. - -dehoni: Please translate the selected code into python and use approbriate packages. - -GitHub Copilot: Here is the translated code into Python using the `gettext` package for handling localization and resource management: - -```python - - -import gettext -import os - -class Resources: - def __init__(self): - self.resource_man = None - self.resource_culture = None - - @property - def resource_manager(self): - if self.resource_man is None: - locale_dir = os.path.join(os.path.dirname(__file__), "locale") - self.resource_man = gettext.translation("resources", localedir=locale_dir, languages=[self.resource_culture]) - return self.resource_man - - @property - def culture(self): - return self.resource_culture - - @culture.setter - def culture(self, value): - self.resource_culture = value - self.resource_man = None # Reset the resource manager to reload with the new culture - -# Example usage -resources = Resources() -resources.culture = "en_US" # Set the desired culture -_ = resources.resource_manager.gettext - -# Example of using the localized string -print(_("Hello, World!")) -``` - -In this Python version: -- The `gettext` package is used for localization and resource management. -- The - -Resources - - class manages the resource manager and culture settings. -- The `resource_manager` property initializes the `gettext.translation` object based on the specified culture. -- The `culture` property allows setting the desired culture and resets the resource manager to reload with the new culture. -- An example usage is provided to demonstrate how to set the culture and use the localized strings. - -dehoni: Please translate the selected code into python and use approbriate packages. - -GitHub Copilot: Here is the translated code into Python using the `configparser` package for handling resource files: - -```python - - -[resheader] -resmimetype = text/microsoft-resx -version = 2.0 -reader = System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 -writer = System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - -[data] -Name1 = this is my long string -Color1 = Blue -Bitmap1 = [base64 mime encoded serialized .NET Framework object] -Icon1 = [base64 mime encoded string representing a byte array form of the .NET Framework object] - -[metadata] -Name1.comment = this is a comment -Icon1.comment = This is a comment -``` - -In this Python version: -- The `.resx` file is translated into an `.ini` file format using the `configparser` package. -- The `resheader` section contains metadata about the resource file. -- The `data` section contains the actual resource data. -- The `metadata` section contains comments associated with the data. - -To read this `.ini` file in Python, you can use the following code: - -```python - - -import configparser - -config = configparser.ConfigParser() -config.read("resources.ini") - -# Accessing resheader values -resmimetype = config["resheader"]["resmimetype"] -version = config["resheader"]["version"] -reader = config["resheader"]["reader"] -writer = config["resheader"]["writer"] - -# Accessing data values -name1 = config["data"]["Name1"] -color1 = config["data"]["Color1"] -bitmap1 = config["data"]["Bitmap1"] -icon1 = config["data"]["Icon1"] - -# Accessing metadata comments -name1_comment = config["metadata"]["Name1.comment"] -icon1_comment = config["metadata"]["Icon1.comment"] - -print(f"Name1: {name1} (Comment: {name1_comment})") -print(f"Color1: {color1}") -print(f"Bitmap1: {bitmap1}") -print(f"Icon1: {icon1} (Comment: {icon1_comment})") -``` - -This code reads the `resources.ini` file and prints the values and comments for the resources. - -dehoni: Please translate the selected code into python and use approbriate packages. - -GitHub Copilot: Here is the translated code into Python using the `configparser` package for handling application settings: - -```python - - -[DEFAULT] -LastInstrument = LARMOR -SampleHeight = 11 -SampleWidth = 11 -Geometry = Flat Plate -RunOrder = Alternate - TRANS first -NumTrans = 1 -NumSans = 1 -LoopPerRun = False -A2SettingSans = Large = 25, 16 mm -A2SettingTrans = Large = 25, 16 mm -CollectionMode = Events -``` - -To read and write these settings in Python, you can use the following code: - -```python - - -import configparser - -class Settings: - def __init__(self, filename="settings.ini"): - self.config = configparser.ConfigParser() - self.filename = filename - self.config.read(self.filename) - - @property - def last_instrument(self): - return self.config["DEFAULT"]["LastInstrument"] - - @last_instrument.setter - def last_instrument(self, value): - self.config["DEFAULT"]["LastInstrument"] = value - self.save() - - @property - def sample_height(self): - return self.config["DEFAULT"]["SampleHeight"] - - @sample_height.setter - def sample_height(self, value): - self.config["DEFAULT"]["SampleHeight"] = value - self.save() - - @property - def sample_width(self): - return self.config["DEFAULT"]["SampleWidth"] - - @sample_width.setter - def sample_width(self, value): - self.config["DEFAULT"]["SampleWidth"] = value - self.save() - - @property - def geometry(self): - return self.config["DEFAULT"]["Geometry"] - - @geometry.setter - def geometry(self, value): - self.config["DEFAULT"]["Geometry"] = value - self.save() - - @property - def run_order(self): - return self.config["DEFAULT"]["RunOrder"] - - @run_order.setter - def run_order(self, value): - self.config["DEFAULT"]["RunOrder"] = value - self.save() - - @property - def num_trans(self): - return self.config["DEFAULT"]["NumTrans"] - - @num_trans.setter - def num_trans(self, value): - self.config["DEFAULT"]["NumTrans"] = value - self.save() - - @property - def num_sans(self): - return self.config["DEFAULT"]["NumSans"] - - @num_sans.setter - def num_sans(self, value): - self.config["DEFAULT"]["NumSans"] = value - self.save() - - @property - def loop_per_run(self): - return self.config["DEFAULT"]["LoopPerRun"] - - @loop_per_run.setter - def loop_per_run(self, value): - self.config["DEFAULT"]["LoopPerRun"] = value - self.save() - - @property - def a2_setting_sans(self): - return self.config["DEFAULT"]["A2SettingSans"] - - @a2_setting_sans.setter - def a2_setting_sans(self, value): - self.config["DEFAULT"]["A2SettingSans"] = value - self.save() - - @property - def a2_setting_trans(self): - return self.config["DEFAULT"]["A2SettingTrans"] - - @a2_setting_trans.setter - def a2_setting_trans(self, value): - self.config["DEFAULT"]["A2SettingTrans"] = value - self.save() - - @property - def collection_mode(self): - return self.config["DEFAULT"]["CollectionMode"] - - @collection_mode.setter - def collection_mode(self, value): - self.config["DEFAULT"]["CollectionMode"] = value - self.save() - - def save(self): - with open(self.filename, "w") as configfile: - self.config.write(configfile) - -# Example usage -settings = Settings() -print(settings.last_instrument) -settings.last_instrument = "LOQ" -print(settings.last_instrument) -``` - -In this Python version: -- The `settings.ini` file is used to store the application settings. -- The - -Settings - - class provides properties to get and set the settings values. -- The `save` method writes the updated settings back to the `settings.ini` file. -- An example usage is provided to demonstrate how to read and update the settings. - -dehoni: Please translate the selected code into python and use approbriate packages. - -GitHub Copilot: Here is the translated code into Python using the `configparser` package for handling application settings: - -```python - - -[DEFAULT] -LastInstrument = LARMOR -SampleHeight = 11 -SampleWidth = 11 -Geometry = Flat Plate -RunOrder = Alternate - TRANS first -NumTrans = 1 -NumSans = 1 -LoopPerRun = False -A2SettingSans = Large = 25, 16 mm -A2SettingTrans = Large = 25, 16 mm -CollectionMode = Events -``` - -To read and write these settings in Python, you can use the following code: - -```python - - -import configparser - -class Settings: - def __init__(self, filename="settings.ini"): - self.config = configparser.ConfigParser() - self.filename = filename - self.config.read(self.filename) - - @property - def last_instrument(self): - return self.config["DEFAULT"]["LastInstrument"] - - @last_instrument.setter - def last_instrument(self, value): - self.config["DEFAULT"]["LastInstrument"] = value - self.save() - - @property - def sample_height(self): - return self.config["DEFAULT"]["SampleHeight"] - - @sample_height.setter - def sample_height(self, value): - self.config["DEFAULT"]["SampleHeight"] = value - self.save() - - @property - def sample_width(self): - return self.config["DEFAULT"]["SampleWidth"] - - @sample_width.setter - def sample_width(self, value): - self.config["DEFAULT"]["SampleWidth"] = value - self.save() - - @property - def geometry(self): - return self.config["DEFAULT"]["Geometry"] - - @geometry.setter - def geometry(self, value): - self.config["DEFAULT"]["Geometry"] = value - self.save() - - @property - def run_order(self): - return self.config["DEFAULT"]["RunOrder"] - - @run_order.setter - def run_order(self, value): - self.config["DEFAULT"]["RunOrder"] = value - self.save() - - @property - def num_trans(self): - return self.config["DEFAULT"]["NumTrans"] - - @num_trans.setter - def num_trans(self, value): - self.config["DEFAULT"]["NumTrans"] = value - self.save() - - @property - def num_sans(self): - return self.config["DEFAULT"]["NumSans"] - - @num_sans.setter - def num_sans(self, value): - self.config["DEFAULT"]["NumSans"] = value - self.save() - - @property - def loop_per_run(self): - return self.config["DEFAULT"]["LoopPerRun"] - - @loop_per_run.setter - def loop_per_run(self, value): - self.config["DEFAULT"]["LoopPerRun"] = value - self.save() - - @property - def a2_setting_sans(self): - return self.config["DEFAULT"]["A2SettingSans"] - - @a2_setting_sans.setter - def a2_setting_sans(self, value): - self.config["DEFAULT"]["A2SettingSans"] = value - self.save() - - @property - def a2_setting_trans(self): - return self.config["DEFAULT"]["A2SettingTrans"] - - @a2_setting_trans.setter - def a2_setting_trans(self, value): - self.config["DEFAULT"]["A2SettingTrans"] = value - self.save() - - @property - def collection_mode(self): - return self.config["DEFAULT"]["CollectionMode"] - - @collection_mode.setter - def collection_mode(self, value): - self.config["DEFAULT"]["CollectionMode"] = value - self.save() - - def save(self): - with open(self.filename, "w") as configfile: - self.config.write(configfile) - -# Example usage -settings = Settings() -print(settings.last_instrument) -settings.last_instrument = "LOQ" -print(settings.last_instrument) -``` - -In this Python version: -- The `settings.ini` file is used to store the application settings. -- The `Settings` class provides properties to get and set the settings values. -- The `save` method writes the updated settings back to the `settings.ini` file. -- An example usage is provided to demonstrate how to read and update the settings. - -dehoni: Please translate the selected code into python and use approbriate packages. - -GitHub Copilot: Here is the translated code into Python: - -```python - - -class A2Setting: - X_LARGE = "XLarge = 20x18 mm" - LARGE = "Large = 17 mm" - MEDIUM = "Medium = 12 mm" - SMALL = "Small = 5/1 mm" - TRANS = "Trans = 1 mm" - LARMOR = "" - # LARMOR_LARGE = "Large = 25, 16 mm" - # LARMOR_MEDIUM = "Medium = 20, 14 mm" - # LARMOR_SMALL = "Small = 15, 10 mm" - -# Example usage -if __name__ == "__main__": - print(A2Setting.X_LARGE) - print(A2Setting.LARGE) - print(A2Setting.MEDIUM) - print(A2Setting.SMALL) - print(A2Setting.TRANS) - print(A2Setting.LARMOR) -``` - -In this Python version: -- The - -A2Setting - - class is defined with class attributes for each setting. -- The commented-out settings from the original C# code are also commented out in the Python version. -- An example usage is provided to demonstrate how to access the settings. - -dehoni: Please translate the selected code into python and use approbriate packages. - -GitHub Copilot: Here is the translated code into Python: - -```python - - -class DataCollection: - HISTOGRAM = "Histogram" - EVENTS = "Events" - SESANS = "SESANS" - TRANSSANS_EVENTS = "TRANSSANS_Events" - TRANSSANS_HISTOGRAM = "TRANSSANS_Histogram" - - choices = [ - HISTOGRAM, - TRANSSANS_HISTOGRAM, - EVENTS, - TRANSSANS_EVENTS, - SESANS - ] - - @staticmethod - def get_choices(): - return DataCollection.choices - -# Example usage -if __name__ == "__main__": - print(DataCollection.get_choices()) -``` - -In this Python version: -- The - -DataCollection - - class is defined with class attributes for each data collection type. -- The - -choices - - list contains all the data collection types. -- The `get_choices` static method returns the list of choices. -- An example usage is provided to demonstrate how to access the choices. - -dehoni: Please translate the selected code into python and use approbriate packages. - -GitHub Copilot: Here is the translated code into Python using the `tkinter` package for data binding and validation: - -```python - - -import tkinter as tk - -class Estimation: - def __init__(self): - self._count_rate = tk.DoubleVar(value=40) - self._move_time = tk.DoubleVar(value=120) - self._script_time = tk.StringVar() - self._is_valid = tk.BooleanVar(value=True) - self.property_changed = [] - - self._count_rate.trace_add("write", self.validate) - self._move_time.trace_add("write", self.validate) - - @property - def count_rate(self): - return self._count_rate.get() - - @count_rate.setter - def count_rate(self, value): - self._count_rate.set(value) - self.on_property_changed("count_rate") - - @property - def move_time(self): - return self._move_time.get() - - @move_time.setter - def move_time(self, value): - self._move_time.set(value) - self.on_property_changed("move_time") - - @property - def script_time(self): - return self._script_time.get() - - @script_time.setter - def script_time(self, value): - self._script_time.set(value) - self.on_property_changed("script_time") - - @property - def is_valid(self): - return self._is_valid.get() - - @is_valid.setter - def is_valid(self, value): - self._is_valid.set(value) - self.on_property_changed("is_valid") - - def on_property_changed(self, name): - for callback in self.property_changed: - callback(name) - - def validate(self, *args): - error_message = "" - if self.count_rate < 0: - error_message = "Count rate cannot be negative!" - elif self.move_time < 0: - error_message = "Move time cannot be negative!" - - if error_message: - self.is_valid = False - else: - self.is_valid = True - - return error_message - -# Example usage -if __name__ == "__main__": - def on_property_changed(name): - print(f"Property {name} changed") - - estimation = Estimation() - estimation.property_changed.append(on_property_changed) - - print(estimation.count_rate) - estimation.count_rate = -10 # This should trigger validation and set is_valid to False - print(estimation.is_valid) - estimation.count_rate = 50 # This should trigger validation and set is_valid to True - print(estimation.is_valid) -``` - -In this Python version: -- The - -Estimation - - class uses `tkinter` variable classes (`DoubleVar`, `StringVar`, `BooleanVar`) for data binding. -- The `validate` method checks the validity of `count_rate` and `move_time` and updates the `is_valid` property accordingly. -- The `on_property_changed` method notifies subscribers when a property changes. -- An example usage is provided to demonstrate how to use the - -Estimation - - class and handle property changes. - -dehoni: Please translate the selected code into python and use approbriate packages. - -GitHub Copilot: Here is the translated code into Python using the `tkinter` package for data binding and property change notifications: - -```python - - -import tkinter as tk - -class Experiment: - def __init__(self): - self._position = tk.StringVar() - self._trans = tk.DoubleVar(value=0) - self._trans_wait = tk.StringVar() - self._sans = tk.DoubleVar(value=0) - self._sans_wait = tk.StringVar() - self._period = tk.StringVar() - self._background = tk.StringVar() - self._sample = tk.StringVar() - self._thickness = tk.StringVar() - self._temperature1 = tk.StringVar() - self._temperature2 = tk.StringVar() - self._field = tk.StringVar() - self._shear_rate1 = tk.StringVar() - self._shear_rate2 = tk.StringVar() - self._shear_angle1 = tk.StringVar() - self._shear_angle2 = tk.StringVar() - self._pre_command = tk.StringVar() - self._post_command = tk.StringVar() - self._rb_number = tk.StringVar() - self.property_changed = [] - - self._position.trace_add("write", self.on_property_changed) - self._trans.trace_add("write", self.on_property_changed) - self._trans_wait.trace_add("write", self.on_property_changed) - self._sans.trace_add("write", self.on_property_changed) - self._sans_wait.trace_add("write", self.on_property_changed) - self._period.trace_add("write", self.on_property_changed) - self._background.trace_add("write", self.on_property_changed) - self._sample.trace_add("write", self.on_property_changed) - self._thickness.trace_add("write", self.on_property_changed) - self._temperature1.trace_add("write", self.on_property_changed) - self._temperature2.trace_add("write", self.on_property_changed) - self._field.trace_add("write", self.on_property_changed) - self._shear_rate1.trace_add("write", self.on_property_changed) - self._shear_rate2.trace_add("write", self.on_property_changed) - self._shear_angle1.trace_add("write", self.on_property_changed) - self._shear_angle2.trace_add("write", self.on_property_changed) - self._pre_command.trace_add("write", self.on_property_changed) - self._post_command.trace_add("write", self.on_property_changed) - self._rb_number.trace_add("write", self.on_property_changed) - - def on_property_changed(self, *args): - for callback in self.property_changed: - callback(args[0]) - - @property - def position(self): - return self._position.get() - - @position.setter - def position(self, value): - self._position.set(value) - - @property - def trans(self): - return self._trans.get() - - @trans.setter - def trans(self, value): - self._trans.set(value) - - @property - def trans_wait(self): - return self._trans_wait.get() - - @trans_wait.setter - def trans_wait(self, value): - self._trans_wait.set(value) - - @property - def sans(self): - return self._sans.get() - - @sans.setter - def sans(self, value): - self._sans.set(value) - - @property - def sans_wait(self): - return self._sans_wait.get() - - @sans_wait.setter - def sans_wait(self, value): - self._sans_wait.set(value) - - @property - def period(self): - return self._period.get() - - @period.setter - def period(self, value): - self._period.set(value) - - @property - def background(self): - return self._background.get() - - @background.setter - def background(self, value): - self._background.set(value) - - @property - def sample(self): - return self._sample.get() - - @sample.setter - def sample(self, value): - self._sample.set(value) - - @property - def thickness(self): - return self._thickness.get() - - @thickness.setter - def thickness(self, value): - self._thickness.set(value) - - @property - def temperature1(self): - return self._temperature1.get() - - @temperature1.setter - def temperature1(self, value): - self._temperature1.set(value) - - @property - def temperature2(self): - return self._temperature2.get() - - @temperature2.setter - def temperature2(self, value): - self._temperature2.set(value) - - @property - def field(self): - return self._field.get() - - @field.setter - def field(self, value): - self._field.set(value) - - @property - def shear_rate1(self): - return self._shear_rate1.get() - - @shear_rate1.setter - def shear_rate1(self, value): - self._shear_rate1.set(value) - - @property - def shear_rate2(self): - return self._shear_rate2.get() - - @shear_rate2.setter - def shear_rate2(self, value): - self._shear_rate2.set(value) - - @property - def shear_angle1(self): - return self._shear_angle1.get() - - @shear_angle1.setter - def shear_angle1(self, value): - self._shear_angle1.set(value) - - @property - def shear_angle2(self): - return self._shear_angle2.get() - - @shear_angle2.setter - def shear_angle2(self, value): - self._shear_angle2.set(value) - - @property - def pre_command(self): - return self._pre_command.get() - - @pre_command.setter - def pre_command(self, value): - self._pre_command.set(value) - - @property - def post_command(self): - return self._post_command.get() - - @post_command.setter - def post_command(self, value): - self._post_command.set(value) - - @property - def rb_number(self): - return self._rb_number.get() - - @rb_number.setter - def rb_number(self, value): - self._rb_number.set(value) - -# Example usage -if __name__ == "__main__": - def on_property_changed(name): - print(f"Property {name} changed") - - experiment = Experiment() - experiment.property_changed.append(on_property_changed) - - experiment.position = "New Position" - experiment.trans = 10.5 - experiment.sans = 20.0 - experiment.temperature1 = "25" - experiment.temperature2 = "30" - experiment.field = "1.5T" - experiment.shear_rate1 = "100" - experiment.shear_rate2 = "200" - experiment.shear_angle1 = "45" - experiment.shear_angle2 = "90" - experiment.pre_command = "Pre Command" - experiment.post_command = "Post Command" - experiment.rb_number = "RB12345" -``` - -In this Python version: -- The - -Experiment - - class uses `tkinter` variable classes (`StringVar`, `DoubleVar`) for data binding. -- The `on_property_changed` method notifies subscribers when a property changes. -- Properties are defined for each attribute to get and set the values. -- An example usage is provided to demonstrate how to use the - -Experiment - - class and handle property changes. - -dehoni: Please translate the selected code into python and use approbriate packages. - -GitHub Copilot: Here is the translated code into Python using the `tkinter` package for data binding and property change notifications: - -```python - - -import tkinter as tk -from configparser import ConfigParser - -class ExperimentSettings: - def __init__(self, config_file="settings.ini"): - self.config = ConfigParser() - self.config.read(config_file) - self.config_file = config_file - - self._order = tk.StringVar(value=self.config["DEFAULT"].get("RunOrder", "TransFirst")) - self._num_sans = tk.IntVar(value=self.config["DEFAULT"].getint("NumSans", 1)) - self._num_trans = tk.IntVar(value=self.config["DEFAULT"].getint("NumTrans", 1)) - self._loop_per_run = tk.BooleanVar(value=self.config["DEFAULT"].getboolean("LoopPerRun", False)) - self._a2_setting_sans = tk.StringVar(value=self.config["DEFAULT"].get("A2SettingSans", "Large")) - self._a2_setting_trans = tk.StringVar(value=self.config["DEFAULT"].get("A2SettingTrans", "Large")) - self._sample_geometry = tk.StringVar(value=self.config["DEFAULT"].get("Geometry", "Flat Plate")) - self._sample_height = tk.DoubleVar(value=self.config["DEFAULT"].getfloat("SampleHeight", 11)) - self._sample_width = tk.DoubleVar(value=self.config["DEFAULT"].getfloat("SampleWidth", 11)) - self._sample_width_enabled = tk.BooleanVar(value=True) - self._collection_mode = tk.StringVar(value=self.config["DEFAULT"].get("CollectionMode", "Events")) - self._is_valid = tk.BooleanVar(value=True) - self.property_changed = [] - - self._order.trace_add("write", self.on_property_changed) - self._num_sans.trace_add("write", self.on_property_changed) - self._num_trans.trace_add("write", self.on_property_changed) - self._loop_per_run.trace_add("write", self.on_property_changed) - self._a2_setting_sans.trace_add("write", self.on_property_changed) - self._a2_setting_trans.trace_add("write", self.on_property_changed) - self._sample_geometry.trace_add("write", self.on_property_changed) - self._sample_height.trace_add("write", self.on_property_changed) - self._sample_width.trace_add("write", self.on_property_changed) - self._collection_mode.trace_add("write", self.on_property_changed) - - def on_property_changed(self, *args): - for callback in self.property_changed: - callback(args[0]) - self.save() - - def save(self): - self.config["DEFAULT"]["RunOrder"] = self._order.get() - self.config["DEFAULT"]["NumSans"] = str(self._num_sans.get()) - self.config["DEFAULT"]["NumTrans"] = str(self._num_trans.get()) - self.config["DEFAULT"]["LoopPerRun"] = str(self._loop_per_run.get()) - self.config["DEFAULT"]["A2SettingSans"] = self._a2_setting_sans.get() - self.config["DEFAULT"]["A2SettingTrans"] = self._a2_setting_trans.get() - self.config["DEFAULT"]["Geometry"] = self._sample_geometry.get() - self.config["DEFAULT"]["SampleHeight"] = str(self._sample_height.get()) - self.config["DEFAULT"]["SampleWidth"] = str(self._sample_width.get()) - self.config["DEFAULT"]["CollectionMode"] = self._collection_mode.get() - with open(self.config_file, "w") as configfile: - self.config.write(configfile) - - @property - def order(self): - return self._order.get() - - @order.setter - def order(self, value): - self._order.set(value) - - @property - def num_sans(self): - return self._num_sans.get() - - @num_sans.setter - def num_sans(self, value): - self._num_sans.set(value) - - @property - def num_trans(self): - return self._num_trans.get() - - @num_trans.setter - def num_trans(self, value): - self._num_trans.set(value) - - @property - def loop_per_run(self): - return self._loop_per_run.get() - - @loop_per_run.setter - def loop_per_run(self, value): - self._loop_per_run.set(value) - - @property - def a2_setting_sans(self): - return self._a2_setting_sans.get() - - @a2_setting_sans.setter - def a2_setting_sans(self, value): - self._a2_setting_sans.set(value) - - @property - def a2_setting_trans(self): - return self._a2_setting_trans.get() - - @a2_setting_trans.setter - def a2_setting_trans(self, value): - self._a2_setting_trans.set(value) - - @property - def sample_geometry(self): - return self._sample_geometry.get() - - @sample_geometry.setter - def sample_geometry(self, value): - self._sample_geometry.set(value) - self.sample_width_enabled = value != "Disc" - - @property - def sample_height(self): - return self._sample_height.get() - - @sample_height.setter - def sample_height(self, value): - self._sample_height.set(value) - if not self.sample_width_enabled: - self.sample_width = value - - @property - def sample_width(self): - return self._sample_width.get() - - @sample_width.setter - def sample_width(self, value): - self._sample_width.set(value) - - @property - def sample_width_enabled(self): - return self._sample_width_enabled.get() - - @sample_width_enabled.setter - def sample_width_enabled(self, value): - self._sample_width_enabled.set(value) - if not value: - self.sample_width = self.sample_height - - @property - def collection_mode(self): - return self._collection_mode.get() - - @collection_mode.setter - def collection_mode(self, value): - self._collection_mode.set(value) - - @property - def is_valid(self): - return self._is_valid.get() - - @is_valid.setter - def is_valid(self, value): - self._is_valid.set(value) - - def validate(self): - error_message = "" - if self.num_sans < 0: - error_message = "Number of SANS runs cannot be negative!" - elif self.num_trans < 0: - error_message = "Number of TRANS runs cannot be negative!" - elif self.sample_height <= 0 or self.sample_height > 20: - error_message = "Sample height must be between 0 and 20" - elif self.sample_width <= 0 or self.sample_width > 20: - error_message = "Sample width must be between 0 and 20" - - if error_message: - self.is_valid = False - else: - self.is_valid = True - - return error_message - -# Example usage -if __name__ == "__main__": - def on_property_changed(name): - print(f"Property {name} changed") - - settings = ExperimentSettings() - settings.property_changed.append(on_property_changed) - - settings.order = "SansFirst" - settings.num_sans = 2 - settings.num_trans = 3 - settings.sample_height = 15 - settings.sample_width = 10 - settings.collection_mode = "Histogram" - print(settings.validate()) -``` - -In this Python version: -- The - -ExperimentSettings - - class uses `tkinter` variable classes (`StringVar`, `IntVar`, `DoubleVar`, `BooleanVar`) for data binding. -- The `on_property_changed` method notifies subscribers when a property changes and saves the settings to the `settings.ini` file. -- Properties are defined for each attribute to get and set the values. -- The `validate` method checks the validity of the settings and updates the `is_valid` property accordingly. -- An example usage is provided to demonstrate how to use the - -ExperimentSettings - - class and handle property changes. - -dehoni: Please translate the selected code into python and use approbriate packages. - -GitHub Copilot: Here is the translated code into Python: - -```python - - -class Geometry: - DISC = "Disc" - CYLINDRICAL = "Cylindrical" - FLAT_PLATE = "Flat Plate" - SINGLE_CRYSTAL = "Single Crystal" - - choices = [ - DISC, - CYLINDRICAL, - FLAT_PLATE, - SINGLE_CRYSTAL - ] - - @staticmethod - def get_choices(): - return Geometry.choices - -# Example usage -if __name__ == "__main__": - print(Geometry.get_choices()) -``` - -In this Python version: -- The - -Geometry - - class is defined with class attributes for each geometry type. -- The - -choices - - list contains all the geometry types. -- The `get_choices` static method returns the list of choices. -- An example usage is provided to demonstrate how to access the choices. - -dehoni: Please translate the selected code into python and use approbriate packages. - -GitHub Copilot: Here is the translated code into Python using the `tkinter` package for data binding and property change notifications: - -```python - - -import tkinter as tk -from tkinter import StringVar, BooleanVar -from typing import List - -class InstrumentInfo: - property_changed = [] - static_property_changed = [] - - def __init__(self): - self._a2_label = StringVar(value="A2 Setting:") - self._a2_choices = tk.Variable(value=["Large", "Medium", "Small"]) - self._a2_trans_enabled = BooleanVar(value=True) - self._collection_mode_enabled = BooleanVar(value=False) - self._script_generator = None - - self._a2_label.trace_add("write", self.on_property_changed) - self._a2_choices.trace_add("write", self.on_property_changed) - self._a2_trans_enabled.trace_add("write", self.on_property_changed) - self._collection_mode_enabled.trace_add("write", self.on_property_changed) - - def on_property_changed(self, *args): - for callback in self.property_changed: - callback(args[0]) - - @staticmethod - def on_static_property_changed(property_name): - for callback in InstrumentInfo.static_property_changed: - callback(property_name) - - @property - def a2_label(self): - return self._a2_label.get() - - @a2_label.setter - def a2_label(self, value): - self._a2_label.set(value) - self.on_static_property_changed("a2_label") - - @property - def a2_choices(self): - return self._a2_choices.get() - - @a2_choices.setter - def a2_choices(self, value: List[str]): - self._a2_choices.set(value) - self.on_static_property_changed("a2_choices") - - @property - def a2_trans_enabled(self): - return self._a2_trans_enabled.get() - - @a2_trans_enabled.setter - def a2_trans_enabled(self, value): - self._a2_trans_enabled.set(value) - self.on_static_property_changed("a2_trans_enabled") - - @property - def collection_mode_enabled(self): - return self._collection_mode_enabled.get() - - @collection_mode_enabled.setter - def collection_mode_enabled(self, value): - self._collection_mode_enabled.set(value) - self.on_static_property_changed("collection_mode_enabled") - - @property - def script_generator(self): - return self._script_generator - - @script_generator.setter - def script_generator(self, value): - self._script_generator = value - -# Example usage -if __name__ == "__main__": - def on_property_changed(name): - print(f"Property {name} changed") - - def on_static_property_changed(name): - print(f"Static property {name} changed") - - instrument_info = InstrumentInfo() - instrument_info.property_changed.append(on_property_changed) - InstrumentInfo.static_property_changed.append(on_static_property_changed) - - instrument_info.a2_label = "New A2 Setting:" - instrument_info.a2_choices = ["XLarge", "Large", "Medium", "Small"] - instrument_info.a2_trans_enabled = False - instrument_info.collection_mode_enabled = True -``` - -In this Python version: -- The - -InstrumentInfo - - class uses `tkinter` variable classes (`StringVar`, `BooleanVar`) for data binding. -- The `on_property_changed` method notifies subscribers when a property changes. -- The `on_static_property_changed` method notifies subscribers when a static property changes. -- Properties are defined for each attribute to get and set the values. -- An example usage is provided to demonstrate how to use the - -InstrumentInfo - - class and handle property changes. - -dehoni: Please translate the selected code into python and use approbriate packages. - -GitHub Copilot: Here is the translated code into Python: - -```python - - -class RunOrder: - ALL_TRANS = "All TRANS first" - TRANS_FIRST = "Alternate - TRANS first" - ALL_SANS = "All SANS first" - SANS_FIRST = "Alternate - SANS first" - - choices = [ - ALL_TRANS, - TRANS_FIRST, - ALL_SANS, - SANS_FIRST - ] - - @staticmethod - def get_choices(): - return RunOrder.choices - -# Example usage -if __name__ == "__main__": - print(RunOrder.get_choices()) -``` - -In this Python version: -- The - -RunOrder - - class is defined with class attributes for each run order type. -- The - -choices - - list contains all the run order types. -- The `get_choices` static method returns the list of choices. -- An example usage is provided to demonstrate how to access the choices. - -dehoni: Please translate the selected code into python and use approbriate packages. - -GitHub Copilot: Here is the translated code into Python: - -```python - - -class WaitForUnits: - MICROAMPS = "Microamp-hours" - MINUTES = "Minutes" - SECONDS = "Seconds" - FRAMES = "Frames" - - choices = [ - MICROAMPS, - MINUTES, - SECONDS, - FRAMES - ] - - @staticmethod - def get_choices(): - return WaitForUnits.choices - -# Example usage -if __name__ == "__main__": - print(WaitForUnits.get_choices()) -``` - -In this Python version: -- The - -WaitForUnits - - class is defined with class attributes for each wait unit type. -- The - -choices - - list contains all the wait unit types. -- The `get_choices` static method returns the list of choices. -- An example usage is provided to demonstrate how to access the choices. - -dehoni: Please translate the selected code into python and use approbriate packages. - -GitHub Copilot: Here is the translated code into Python: - -```python - - -import csv - -class CsvExporter: - @staticmethod - def export(file, runs): - with open(file, mode="w", newline="") as csvfile: - writer = csv.writer(csvfile) - for exp in runs: - row = [ - exp.position, - str(exp.trans), - exp.trans_wait, - str(exp.sans), - exp.sans_wait, - exp.period, - exp.sample, - exp.background, - exp.thickness, - exp.temperature1, - exp.temperature2, - exp.field, - exp.shear_rate1, - exp.shear_rate2, - exp.shear_angle1, - exp.shear_angle2, - exp.pre_command, - exp.post_command, - exp.rb_number - ] - writer.writerow(row) - -# Example usage -class Experiment: - def __init__(self, position="", trans=0, trans_wait="", sans=0, sans_wait="", period="", sample="", background="", thickness="", temperature1="", temperature2="", field="", shear_rate1="", shear_rate2="", shear_angle1="", shear_angle2="", pre_command="", post_command="", rb_number=""): - self.position = position - self.trans = trans - self.trans_wait = trans_wait - self.sans = sans - self.sans_wait = sans_wait - self.period = period - self.sample = sample - self.background = background - self.thickness = thickness - self.temperature1 = temperature1 - self.temperature2 = temperature2 - self.field = field - self.shear_rate1 = shear_rate1 - self.shear_rate2 = shear_rate2 - self.shear_angle1 = shear_angle1 - self.shear_angle2 = shear_angle2 - self.pre_command = pre_command - self.post_command = post_command - self.rb_number = rb_number - -if __name__ == "__main__": - runs = [ - Experiment(position="pos1", trans=10, trans_wait="Minutes", sans=20, sans_wait="Minutes", period="1", sample="Sample1", background="Background1", thickness="1.0", temperature1="25", temperature2="30", field="1.5T", shear_rate1="100", shear_rate2="200", shear_angle1="45", shear_angle2="90", pre_command="Pre Command", post_command="Post Command", rb_number="RB12345") - ] - CsvExporter.export("output.csv", runs) -``` - -In this Python version: -- The - -CsvExporter - - class uses the `csv` module to write data to a CSV file. -- The `export` method writes the properties of each - -Experiment - - object to the CSV file. -- An example - -Experiment - - class and usage are provided to demonstrate how to use the - -CsvExporter - - class. - -dehoni: Please translate the selected code into python and use approbriate packages. - -GitHub Copilot: Here is the translated code into Python using the `pandas` package for handling Excel files: - -```python - - -import pandas as pd -from tkinter import messagebox - -class ExcelIO: - columns = [ - "POSITION", "TRANS", "TRANS_WAIT_FOR", "SANS", "SANS_WAIT_FOR", "PERIOD", "SAMPLE_IDENTIFICATION_STRING", "BACKGROUND", - "THICKNESS", "TEMPERATURE", "TEMPERATURE2", "FIELD", "SHEAR_RATE_1", "SHEAR_RATE_2", "SHEAR_ANGLE_1", "SHEAR_ANGLE_2", - "PRE_COMMAND", "POST_COMMAND", "RB_NUMBER" - ] - - def __init__(self): - self.dataset = None - - def open_excel_workbook(self, filename): - try: - self.dataset = pd.read_excel(filename, sheet_name=None) - except Exception as ex: - messagebox.showerror("SANS Script - Error", str(ex)) - raise IOError("Could not open Excel spreadsheet") - - def extract_experiments(self): - experiments = [] - if self.dataset is not None: - for sheet_name, df in self.dataset.items(): - for _, row in df.iterrows(): - exp = Experiment( - position=row.get("POSITION", ""), - trans=self.cast_to_double(row.get("TRANS", 0)), - trans_wait=row.get("TRANS_WAIT_FOR", ""), - sans=self.cast_to_double(row.get("SANS", 0)), - sans_wait=row.get("SANS_WAIT_FOR", ""), - period=row.get("PERIOD", ""), - sample=row.get("SAMPLE_IDENTIFICATION_STRING", ""), - background=row.get("BACKGROUND", ""), - thickness=row.get("THICKNESS", ""), - temperature1=row.get("TEMPERATURE", ""), - temperature2=row.get("TEMPERATURE2", ""), - field=row.get("FIELD", ""), - shear_rate1=row.get("SHEAR_RATE_1", ""), - shear_rate2=row.get("SHEAR_RATE_2", ""), - shear_angle1=row.get("SHEAR_ANGLE_1", ""), - shear_angle2=row.get("SHEAR_ANGLE_2", ""), - pre_command=row.get("PRE_COMMAND", ""), - post_command=row.get("POST_COMMAND", ""), - rb_number=row.get("RB_NUMBER", "") - ) - experiments.append(exp) - return experiments - - @staticmethod - def cast_to_double(val): - try: - return float(val) - except (ValueError, TypeError): - return 0.0 - - def export_data_to_excel(self, runs, filename): - data = [] - for exp in runs: - data.append([ - exp.position, exp.trans, exp.trans_wait, exp.sans, exp.sans_wait, - exp.period, exp.sample, exp.background, exp.thickness, exp.temperature1, exp.temperature2, exp.field, - exp.shear_rate1, exp.shear_rate2, exp.shear_angle1, exp.shear_angle2, exp.pre_command, - exp.post_command, exp.rb_number - ]) - df = pd.DataFrame(data, columns=self.columns) - df.to_excel(filename, index=False) - -# Example usage -class Experiment: - def __init__(self, position="", trans=0, trans_wait="", sans=0, sans_wait="", period="", sample="", background="", thickness="", temperature1="", temperature2="", field="", shear_rate1="", shear_rate2="", shear_angle1="", shear_angle2="", pre_command="", post_command="", rb_number=""): - self.position = position - self.trans = trans - self.trans_wait = trans_wait - self.sans = sans - self.sans_wait = sans_wait - self.period = period - self.sample = sample - self.background = background - self.thickness = thickness - self.temperature1 = temperature1 - self.temperature2 = temperature2 - self.field = field - self.shear_rate1 = shear_rate1 - self.shear_rate2 = shear_rate2 - self.shear_angle1 = shear_angle1 - self.shear_angle2 = shear_angle2 - self.pre_command = pre_command - self.post_command = post_command - self.rb_number = rb_number - -if __name__ == "__main__": - runs = [ - Experiment(position="pos1", trans=10, trans_wait="Minutes", sans=20, sans_wait="Minutes", period="1", sample="Sample1", background="Background1", thickness="1.0", temperature1="25", temperature2="30", field="1.5T", shear_rate1="100", shear_rate2="200", shear_angle1="45", shear_angle2="90", pre_command="Pre Command", post_command="Post Command", rb_number="RB12345") - ] - excel_io = ExcelIO() - excel_io.export_data_to_excel(runs, "output.xlsx") - excel_io.open_excel_workbook("output.xlsx") - experiments = excel_io.extract_experiments() - for exp in experiments: - print(exp.__dict__) -``` - -In this Python version: -- The - -ExcelIO - - class uses the `pandas` package to read and write Excel files. -- The `open_excel_workbook` method reads the Excel file into a `pandas` DataFrame. -- The `extract_experiments` method extracts experiment data from the DataFrame. -- The `export_data_to_excel` method writes experiment data to an Excel file. -- An example - -Experiment - - class and usage are provided to demonstrate how to use the - -ExcelIO - - class. - -dehoni: Please translate the selected code into python and use approbriate packages. - -GitHub Copilot: Here is the translated code into Python using the `tkinter` package for creating a similar dialog window: - -```python - - -import tkinter as tk -from tkinter import scrolledtext - -class PreviewDialog(tk.Toplevel): - def __init__(self, parent): - super().__init__(parent) - self.title("PreviewDialog") - self.geometry("600x500") - - self.grid_rowconfigure(0, weight=1) - self.grid_columnconfigure(0, weight=1) - - # Create a frame for the text area with a border - border_frame = tk.Frame(self, borderwidth=1, relief="solid") - border_frame.grid(row=0, column=0, padx=5, pady=5, sticky="nsew") - - # Create a scrollable text area - self.txt_script = scrolledtext.ScrolledText(border_frame, wrap=tk.WORD) - self.txt_script.pack(fill=tk.BOTH, expand=True) - - # Create a frame for the buttons - button_frame = tk.Frame(self) - button_frame.grid(row=1, column=0, pady=5, sticky="e") - - # Create the Print Script button (initially hidden) - self.btn_print = tk.Button(button_frame, text="Print Script", command=self.print_script) - self.btn_print.pack(side=tk.LEFT, padx=5) - self.btn_print.pack_forget() # Hide the button initially - - # Create the Close button - btn_close = tk.Button(button_frame, text="Close", command=self.close) - btn_close.pack(side=tk.LEFT, padx=5) - - def print_script(self): - # Implement the print functionality here - pass - - def close(self): - self.destroy() - -# Example usage -if __name__ == "__main__": - root = tk.Tk() - root.withdraw() # Hide the root window - - dialog = PreviewDialog(root) - dialog.txt_script.insert(tk.END, "This is a preview of the script.") - dialog.mainloop() -``` - -In this Python version: -- The `PreviewDialog` class is defined using the `tkinter` package. -- A `ScrolledText` widget is used to create a scrollable text area. -- A `Frame` widget is used to create a border around the text area. -- A `Frame` widget is used to create a horizontal stack of buttons. -- The `Print Script` button is initially hidden using the `pack_forget` method. -- The `close` method destroys the dialog window. -- An example usage is provided to demonstrate how to create and display the `PreviewDialog`. - -dehoni: Please translate the selected code into python and use approbriate packages. - -GitHub Copilot: Here is the translated code into Python using the `tkinter` package: - -```python - - -import tkinter as tk -from tkinter import scrolledtext - -class PreviewDialog(tk.Toplevel): - def __init__(self, parent): - super().__init__(parent) - self.title("PreviewDialog") - self.geometry("600x500") - - self.grid_rowconfigure(0, weight=1) - self.grid_columnconfigure(0, weight=1) - - # Create a frame for the text area with a border - border_frame = tk.Frame(self, borderwidth=1, relief="solid") - border_frame.grid(row=0, column=0, padx=5, pady=5, sticky="nsew") - - # Create a scrollable text area - self.txt_script = scrolledtext.ScrolledText(border_frame, wrap=tk.WORD) - self.txt_script.pack(fill=tk.BOTH, expand=True) - - # Create a frame for the buttons - button_frame = tk.Frame(self) - button_frame.grid(row=1, column=0, pady=5, sticky="e") - - # Create the Print Script button (initially hidden) - self.btn_print = tk.Button(button_frame, text="Print Script", command=self.print_script) - self.btn_print.pack(side=tk.LEFT, padx=5) - self.btn_print.pack_forget() # Hide the button initially - - # Create the Close button - btn_close = tk.Button(button_frame, text="Close", command=self.close) - btn_close.pack(side=tk.LEFT, padx=5) - - def set_script(self, script): - self.txt_script.delete(1.0, tk.END) - self.txt_script.insert(tk.END, script) - - def print_script(self): - # Implement the print functionality here - pass - - def close(self): - self.destroy() - -# Example usage -if __name__ == "__main__": - root = tk.Tk() - root.withdraw() # Hide the root window - - dialog = PreviewDialog(root) - dialog.set_script("This is a preview of the script.") - dialog.mainloop() -``` - -In this Python version: -- The - -PreviewDialog - - class is defined using the `tkinter` package. -- The `set_script` method sets the text of the `ScrolledText` widget. -- The `print_script` method is a placeholder for the print functionality. -- The `close` method destroys the dialog window. -- An example usage is provided to demonstrate how to create and display the - -PreviewDialog - -. \ No newline at end of file