diff --git a/CHANGELOG.md b/CHANGELOG.md index 9a76783e..2c633065 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,10 +11,11 @@ Please see [here](https://github.com/hrntsm/Tunny/releases) for the data release ### Added - Support Human-in-the-loop optimization - - Input FishPrint into the objective to start it. - - Add 2 sample gh file. + - Input FishPrint into the objective to start it + - Add 2 sample gh file - Support CMA-ES with Margin - It allows for more efficient optimization in mixed integer problems. +- Open optuna dashboard menu item ### Changed diff --git a/Tunny/TunnyInfo.cs b/Tunny/TunnyInfo.cs index 712a4f2c..8fa4a8b8 100644 --- a/Tunny/TunnyInfo.cs +++ b/Tunny/TunnyInfo.cs @@ -18,14 +18,4 @@ public class Tunny : GH_AssemblyInfo public override string AuthorContact => "contact@hrntsm.com"; public override GH_LibraryLicense License => GH_LibraryLicense.opensource; } - - public class TunnyCategoryIcon : GH_AssemblyPriority - { - public override GH_LoadingInstruction PriorityLoad() - { - Grasshopper.Instances.ComponentServer.AddCategoryIcon("Tunny", Resource.TunnyIcon); - Grasshopper.Instances.ComponentServer.AddCategorySymbolName("Tunny", 'T'); - return GH_LoadingInstruction.Proceed; - } - } } diff --git a/Tunny/UI/LoadingInstruction.cs b/Tunny/UI/LoadingInstruction.cs new file mode 100644 index 00000000..317c84e3 --- /dev/null +++ b/Tunny/UI/LoadingInstruction.cs @@ -0,0 +1,149 @@ +using System; +using System.Collections.Generic; +using System.Drawing; +using System.IO; +using System.Windows.Forms; + +using Grasshopper.GUI; +using Grasshopper.GUI.Canvas; +using Grasshopper.Kernel; + +using Tunny.Resources; +using Tunny.Settings; +using Tunny.Util; + +namespace Tunny.UI +{ + public class LoadingInstruction : GH_AssemblyPriority, IDisposable + { + private ToolStripMenuItem _optunaDashboardToolStripMenuItem; + + public override GH_LoadingInstruction PriorityLoad() + { + Grasshopper.Instances.ComponentServer.AddCategoryIcon("Tunny", Resource.TunnyIcon); + Grasshopper.Instances.ComponentServer.AddCategorySymbolName("Tunny", 'T'); + Grasshopper.Instances.CanvasCreated += RegisterTunnyMenuItems; + return GH_LoadingInstruction.Proceed; + } + + void RegisterTunnyMenuItems(GH_Canvas canvas) + { + Grasshopper.Instances.CanvasCreated -= RegisterTunnyMenuItems; + + GH_DocumentEditor docEditor = Grasshopper.Instances.DocumentEditor; + if (docEditor != null) + { + SetupTunnyMenu(docEditor); + } + } + private void SetupTunnyMenu(GH_DocumentEditor docEditor) + { + ToolStripMenuItem tunnyToolStripMenuItem; + tunnyToolStripMenuItem = new ToolStripMenuItem(); + + docEditor.MainMenuStrip.SuspendLayout(); + + docEditor.MainMenuStrip.Items.AddRange(new ToolStripItem[] { + tunnyToolStripMenuItem + }); + + tunnyToolStripMenuItem.DropDownItems.AddRange(TunnyMenuItems.ToArray()); + tunnyToolStripMenuItem.Name = "TunnyToolStripMenuItem"; + tunnyToolStripMenuItem.Size = new Size(125, 29); + tunnyToolStripMenuItem.Text = "Tunny"; + + docEditor.MainMenuStrip.ResumeLayout(false); + docEditor.MainMenuStrip.PerformLayout(); + + GH_DocumentEditor.AggregateShortcutMenuItems += GH_DocumentEditor_AggregateShortcutMenuItems; + } + + void GH_DocumentEditor_AggregateShortcutMenuItems(object sender, GH_MenuShortcutEventArgs e) + { + e.AppendItem(_optunaDashboardToolStripMenuItem); + } + + private List TunnyMenuItems + { + get + { + var list = new List(); + + _optunaDashboardToolStripMenuItem = new ToolStripMenuItem + { + Name = "OptunaDashboardToolStripMenuItem", + Size = new Size(265, 30), + Text = "Run optuna-dashboard", + }; + _optunaDashboardToolStripMenuItem.Click += OptunaDashboardToolStripMenuItem_Click; + + list.Add(_optunaDashboardToolStripMenuItem); + return list; + } + } + + private void OptunaDashboardToolStripMenuItem_Click(object sender, EventArgs e) + { + string componentFolder = Path.GetDirectoryName(Grasshopper.Instances.ComponentServer.FindObjectByName("Tunny", true, true).Location); + + string pythonDirectory = componentFolder + "/python-3.10.0-embed-amd64"; + string dashboardPath = pythonDirectory + "/Scripts/optuna-dashboard.exe"; + + if (!Directory.Exists(pythonDirectory) && !File.Exists(dashboardPath)) + { + TunnyMessageBox.Show("optuna-dashboard is not installed.\nFirst install optuna-dashboard from the Tunny component.", + "optuna-dashboard is not installed", + MessageBoxButtons.OK, + MessageBoxIcon.Error); + } + else + { + RunOptunaDashboard(componentFolder, dashboardPath); + } + } + + private static void RunOptunaDashboard(string componentFolder, string dashboardPath) + { + string settingsPath = componentFolder + @"\Settings.json"; + string storagePath = string.Empty; + if (File.Exists(settingsPath)) + { + var settings = TunnySettings.Deserialize(File.ReadAllText(settingsPath)); + storagePath = settings.Storage.Path; + } + var ofd = new OpenFileDialog + { + FileName = Path.GetFileName(storagePath), + Filter = @"Journal Storage(*.log)|*.log|SQLite Storage(*.db,*.sqlite)|*.db;*.sqlite", + Title = @"Set Tunny result file path", + }; + if (ofd.ShowDialog() == DialogResult.OK) + { + storagePath = GetStorageArgument(ofd.FileName); + DashboardHandler.RunDashboardProcess(dashboardPath, storagePath); + } + } + + private static string GetStorageArgument(string path) + { + switch (Path.GetExtension(path)) + { + case null: + return string.Empty; + case ".sqlite3": + case ".db": + return @"sqlite:///" + $"\"{path}\""; + case ".log": + return $"\"{path}\""; + default: + throw new NotImplementedException(); + } + } + + public void Dispose() + { + _optunaDashboardToolStripMenuItem.Dispose(); + GC.SuppressFinalize(this); + } + } +} diff --git a/Tunny/UI/OptimizeWindowTab/VisualizeTab.cs b/Tunny/UI/OptimizeWindowTab/VisualizeTab.cs index 4431a371..8979b077 100644 --- a/Tunny/UI/OptimizeWindowTab/VisualizeTab.cs +++ b/Tunny/UI/OptimizeWindowTab/VisualizeTab.cs @@ -1,5 +1,4 @@ using System; -using System.Diagnostics; using System.IO; using System.Linq; @@ -20,30 +19,7 @@ private void DashboardButton_Click(object sender, EventArgs e) return; } - CheckExistDashboardProcess(); - var dashboard = new Process(); - dashboard.StartInfo.FileName = PythonInstaller.GetEmbeddedPythonPath() + @"\Scripts\optuna-dashboard.exe"; - dashboard.StartInfo.Arguments = _settings.Storage.GetOptunaStorageCommandLinePathByExtension(); - dashboard.StartInfo.UseShellExecute = false; - dashboard.StartInfo.WindowStyle = ProcessWindowStyle.Minimized; - dashboard.Start(); - - var browser = new Process(); - browser.StartInfo.FileName = @"http://127.0.0.1:8080/"; - browser.StartInfo.UseShellExecute = true; - browser.Start(); - } - - private static void CheckExistDashboardProcess() - { - Process[] dashboardProcess = Process.GetProcessesByName("optuna-dashboard"); - if (dashboardProcess.Length > 0) - { - foreach (Process p in dashboardProcess) - { - p.Kill(); - } - } + DashboardHandler.RunDashboardProcess(PythonInstaller.GetEmbeddedPythonPath() + @"\Scripts\optuna-dashboard.exe", _settings.Storage.GetOptunaStorageCommandLinePathByExtension()); } private void VisualizeTargetStudy_Changed(object sender, EventArgs e) diff --git a/Tunny/Util/DashboardHandler.cs b/Tunny/Util/DashboardHandler.cs new file mode 100644 index 00000000..55f1c8e6 --- /dev/null +++ b/Tunny/Util/DashboardHandler.cs @@ -0,0 +1,35 @@ +using System.Diagnostics; + +namespace Tunny.Util +{ + public static class DashboardHandler + { + public static void RunDashboardProcess(string dashboardPath, string storageArgument) + { + CheckExistDashboardProcess(); + var dashboard = new Process(); + dashboard.StartInfo.FileName = dashboardPath; + dashboard.StartInfo.Arguments = storageArgument; + dashboard.StartInfo.UseShellExecute = false; + dashboard.StartInfo.WindowStyle = ProcessWindowStyle.Minimized; + dashboard.Start(); + + var browser = new Process(); + browser.StartInfo.FileName = @"http://127.0.0.1:8080/"; + browser.StartInfo.UseShellExecute = true; + browser.Start(); + } + + private static void CheckExistDashboardProcess() + { + Process[] dashboardProcess = Process.GetProcessesByName("optuna-dashboard"); + if (dashboardProcess.Length > 0) + { + foreach (Process p in dashboardProcess) + { + p.Kill(); + } + } + } + } +}