24 private Builder _builder;
25 private List<ImageView> viewers =
new List<ImageView>();
26 private const int MaxRecentImages = 10;
27 private readonly List<string> recentImages =
new List<string>();
28 private readonly Menu recentImagesMenu =
new Menu();
29 private MenuItem openRecentMenu;
30 private MenuItem clearRecentImagesMenu;
31 private static readonly
string RecentImagesPath = System.IO.Path.Combine(
32 Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
33 "BioGTK",
"recent-images.json");
34 private static string Q(
string s)
36 return "\"" + s.Replace(
"\\",
"\\\\").Replace(
"\"",
"\\\"") +
"\"";
38 private static string Bool(
bool value)
40 return value.ToString().ToLowerInvariant();
42 private static string StrArray(
string[] values)
44 return "new string[] { " +
string.Join(
", ", values.Select(Q)) +
" }";
52 for (
int v = 0; v < viewers.Count; v++)
54 for (
int i = 0; i < viewers[v].Images.Count; i++)
56 if (viewers[v].Images[i].Filename == name)
62 public int GetViewerCount()
75 private static string GetSeriesDisplayName(
string file,
int series,
int seriesCount)
78 return System.IO.Path.GetFileName(file);
80 string baseName = System.IO.Path.GetFileNameWithoutExtension(file);
81 string ext = System.IO.Path.GetExtension(file);
82 if (
string.IsNullOrWhiteSpace(ext))
84 return $
"{baseName}_s{series}{ext}";
86 private static bool IsRemoteZarrSource(BioImage image)
88 if (image ==
null || !image.IsZarrSource)
91 string source = image.SourceFile;
92 return !
string.IsNullOrWhiteSpace(source) &&
93 (source.StartsWith(
"https://", StringComparison.OrdinalIgnoreCase) ||
94 source.StartsWith(
"http://", StringComparison.OrdinalIgnoreCase) ||
95 source.StartsWith(
"s3://", StringComparison.OrdinalIgnoreCase));
98 private static bool IsZarrLikeSource(
string file)
100 if (
string.IsNullOrWhiteSpace(file))
103 return file.Contains(
".zarr", StringComparison.OrdinalIgnoreCase) ||
104 file.StartsWith(
"https://", StringComparison.OrdinalIgnoreCase) ||
105 file.StartsWith(
"http://", StringComparison.OrdinalIgnoreCase) ||
106 file.StartsWith(
"s3://", StringComparison.OrdinalIgnoreCase);
109 private static void EnsureLargeZarrUsesPyramidalViewer(BioImage image)
111 if (image ==
null || !image.IsZarrSource || image.Resolutions.Count == 0)
114 image.PyramidNativeLevelCount = image.Resolutions.Count;
115 if (image.Resolutions.Count > 1)
118 const int largeImageDimension = 2048;
119 const int pyramidTargetDimension = 1024;
120 Resolution baseResolution = image.Resolutions[0];
121 if (baseResolution.SizeX <= largeImageDimension &&
122 baseResolution.SizeY <= largeImageDimension)
127 Resolution previous = baseResolution;
128 int width = baseResolution.SizeX;
129 int height = baseResolution.SizeY;
130 while (width > pyramidTargetDimension || height > pyramidTargetDimension)
132 width = Math.Max(1, (width + 1) / 2);
133 height = Math.Max(1, (height + 1) / 2);
134 previous =
new Resolution(
137 previous.PixelFormat,
138 previous.PhysicalSizeX * 2,
139 previous.PhysicalSizeY * 2,
140 previous.PhysicalSizeZ,
143 previous.StageSizeZ);
144 image.Resolutions.Add(previous);
149 var slideImage =
new SlideImage { BioImage = image };
150 image.SlideBase =
new SlideBase(image, slideImage);
153 private static async Task ApplyRemoteZarrDefaultCoordinateAsync(BioImage image,
string source)
155 if (image ==
null ||
string.IsNullOrWhiteSpace(source))
157 if (!source.StartsWith(
"https://", StringComparison.OrdinalIgnoreCase) &&
158 !source.StartsWith(
"http://", StringComparison.OrdinalIgnoreCase) &&
159 !source.StartsWith(
"s3://", StringComparison.OrdinalIgnoreCase))
166 string metadataRoot = source;
167 if (metadataRoot.StartsWith(
"s3://", StringComparison.OrdinalIgnoreCase))
169 metadataRoot =
"https://uk1s3.embassy.ebi.ac.uk/" +
170 metadataRoot.Substring(
"s3://".Length).TrimStart(
'/');
173 using var http =
new System.Net.Http.HttpClient();
174 string json = await http.GetStringAsync(metadataRoot.TrimEnd(
'/') +
"/zarr.json")
175 .ConfigureAwait(
false);
176 using var document = JsonDocument.Parse(json);
178 JsonElement root = document.RootElement;
179 JsonElement omero =
default;
180 bool foundOmero =
false;
181 if (root.TryGetProperty(
"attributes", out JsonElement attributes))
183 if (attributes.TryGetProperty(
"ome", out JsonElement ome) &&
184 ome.TryGetProperty(
"omero", out omero))
188 else if (attributes.TryGetProperty(
"omero", out omero))
193 if (!foundOmero && root.TryGetProperty(
"omero", out omero))
195 if (!foundOmero || !omero.TryGetProperty(
"rdefs", out JsonElement rdefs))
198 int defaultZ = image.Coordinate.Z;
199 int defaultT = image.Coordinate.T;
200 if (rdefs.TryGetProperty(
"defaultZ", out JsonElement zValue) && zValue.TryGetInt32(out
int z))
202 if (rdefs.TryGetProperty(
"defaultT", out JsonElement tValue) && tValue.TryGetInt32(out
int t))
205 defaultZ = Math.Clamp(defaultZ, 0, Math.Max(0, image.SizeZ - 1));
206 defaultT = Math.Clamp(defaultT, 0, Math.Max(0, image.SizeT - 1));
207 image.Coordinate =
new ZCT(defaultZ, image.Coordinate.C, defaultT);
209 catch (Exception ex) when (
210 ex is System.Net.Http.HttpRequestException ||
211 ex is TaskCanceledException ||
214 Console.WriteLine(
"Could not read the OME-Zarr default coordinate: " + ex.Message);
218 private async Task<BioImage?> OpenSeriesAsTabAsync(
228 bool addToRegistry =
true)
230 BioImage im = await BioImage.OpenFile(file, series, tab,
false, tile, tileX, tileY, tileSizeX, tileSizeY);
235 im.Rename(GetSeriesDisplayName(file, series, seriesCount));
244 private async Task<BioImage?> OpenSeriesAsViewAsync(
249 BioImage im = await BioImage.OpenFile(file, series,
false,
false,
false, 0, 0, 0, 0);
253 ShowImageView(im, file, series, seriesCount);
257 private async Task<List<BioImage>> CollectPyramidRootsAsync(
string file)
259 if (IsZarrLikeSource(file))
261 BioImage? zarrImage = await BioImage.OpenFile(file, 0,
false,
false,
false, 0, 0, 0, 0);
262 List<BioImage> singleRoot =
new List<BioImage>();
263 if (zarrImage !=
null)
265 EnsureLargeZarrUsesPyramidalViewer(zarrImage);
266 await ApplyRemoteZarrDefaultCoordinateAsync(zarrImage, file);
267 singleRoot.Add(zarrImage);
275 seriesCount = Math.Max(1, BioImage.GetSeriesCount(file));
282 List<BioImage?> seriesImages =
new List<BioImage?>(seriesCount);
283 for (
int series = 0; series < seriesCount; series++)
285 BioImage? im = await BioImage.OpenFile(file, series,
false,
false,
false, 0, 0, 0, 0);
286 seriesImages.Add(im);
289 List<BioImage> roots =
new List<BioImage>();
290 for (
int series = 0; series < seriesImages.Count; series++)
292 BioImage? cur = seriesImages[series];
293 BioImage? prev = series > 0 ? seriesImages[series - 1] :
null;
294 if (series > 0 && IsContinuationSeries(prev, cur))
307 private static bool IsContinuationSeries(BioImage? prev, BioImage? cur)
309 if (prev ==
null || cur ==
null)
311 if (prev.Resolutions.Count == 0 || cur.Resolutions.Count == 0)
314 Resolution pr = prev.Resolutions[0];
315 Resolution cr = cur.Resolutions[0];
316 return pr.SizeX > cr.SizeX &&
317 pr.SizeY > cr.SizeY &&
318 pr.PixelFormat == cr.PixelFormat;
321 private void ShowImageView(BioImage im,
string file,
int series,
int seriesCount)
324 im.Rename(GetSeriesDisplayName(file, series, seriesCount));
328 view.RefreshZarrLabelOverlays(file);
329 if (!viewers.Contains(view))
332 Label dummy =
new Gtk.Label(im.Filename);
333 dummy.Text = im.Filename;
334 dummy.UseMarkup =
false;
335 dummy.Visible =
false;
336 Label l =
new Gtk.Label(im.Filename);
337 l.Text = im.Filename;
339 tabsView.AppendPage(dummy, l);
341 RememberRecentImage(file);
344 private async Task OpenPyramidRootsAsViewsAsync(
string file,
bool addToRegistry =
false)
346 List<BioImage> roots = await CollectPyramidRootsInBackgroundAsync(file);
347 for (
int series = 0; series < roots.Count; series++)
350 Images.AddImage(roots[series]);
351 ShowImageView(roots[series], file, series, roots.Count);
355 private async Task OpenPyramidRootsAsTabsAsync(
string file)
357 List<BioImage> roots = await CollectPyramidRootsInBackgroundAsync(file);
358 for (
int series = 0; series < roots.Count; series++)
360 BioImage im = roots[series];
362 im.Rename(GetSeriesDisplayName(file, series, roots.Count));
368 private Task<List<BioImage>> CollectPyramidRootsInBackgroundAsync(
string file)
370 return Task.Run(async () => await CollectPyramidRootsAsync(file));
373#pragma warning disable 649
375 public MenuBar ImageJMenu;
377 private MenuItem recentMenu;
379 private MenuItem commandsMenu;
381 private MenuItem runMenu;
383 private MenuItem searchMenu;
385 private MenuItem updateMenu;
387 private MenuItem renameMenu;
389 public MenuBar MainMenu;
391 private MenuItem openURLMenu;
393 private MenuItem openImagesMenu;
395 private MenuItem openOMEImagesMenu;
397 private MenuItem openOMESeriesMenu;
399 private MenuItem openSeriesMenu;
401 private MenuItem openZarr;
403 private MenuItem openQuPathProject;
405 private MenuItem addImagesToTabMenu;
407 private MenuItem saveNumPyBut;
409 private MenuItem addOMEImagesToTab;
411 private MenuItem saveSelectedTiff;
413 private MenuItem saveSelectedOME;
415 private MenuItem saveTabOME;
417 private MenuItem saveTabTiffMenu;
419 private MenuItem saveSeriesMenu;
421 private MenuItem savePyramidalMenu;
423 private MenuItem saveZarr;
425 private MenuItem saveQuPathProject;
427 private MenuItem imagesToStack;
429 private MenuItem uploadImageMenu;
432 private Gtk.CheckMenuItem rgbMenu;
434 private Gtk.CheckMenuItem filteredMenu;
436 private Gtk.CheckMenuItem rawMenu;
438 private Gtk.CheckMenuItem emissionMenu;
441 private MenuItem toolsMenu;
443 private MenuItem setToolMenu;
445 private MenuItem runSAMMenu;
447 private MenuItem plateToolMenu;
450 private MenuItem roiManagerMenu;
452 private MenuItem exportROIsToCSVMenu;
454 private MenuItem importROIsFromCSVMenu;
456 private MenuItem exportROIsOfFolderOfImagesMenu;
458 private MenuItem importROIsFromImageJMenu;
460 private MenuItem exportROIsFromImageJMenu;
462 private MenuItem importROIQuPath;
464 private MenuItem exportROIQuPath;
466 private MenuItem importPointsNapari;
468 private MenuItem importShapesNapari;
470 private MenuItem exportROINapari;
474 private MenuItem autoThresholdAllMenu;
476 private MenuItem channelsToolMenu;
478 private MenuItem switchRedBlueMenu;
481 private MenuItem rotateFlipMenu;
483 private MenuItem stackToolMenu;
485 private MenuItem focusMenu;
487 private MenuItem extractRegionMenu;
490 private MenuItem to8BitMenu;
492 private MenuItem to16BitMenu;
494 private MenuItem to24BitMenu;
496 private MenuItem to32BitMenu;
498 private MenuItem to48BitMenu;
500 private MenuItem toFloatMenu;
502 private MenuItem toShortMenu;
505 private MenuItem filtersMenu;
508 private MenuItem functionsToolMenu;
510 private MenuItem consoleMenu;
512 private MenuItem scriptRunnerMenu;
514 private MenuItem scriptRecorderMenu;
517 private MenuItem aboutMenu;
520 private Notebook tabsView;
523 private Menu contextMenu;
525 private MenuItem tabClose;
527 private MenuItem tabDuplicate;
529 private MenuItem runMicroSAMMenu;
531 private MenuItem omeroMenu;
533 private MenuItem AIMenu;
535#pragma warning restore 649
539 #region Constructors / Destructors
547 Builder builder =
new Builder(
new FileStream(System.IO.Path.GetDirectoryName(Environment.ProcessPath) +
"/" +
"Glade/TabsView.glade", FileMode.Open));
548 return new TabsView(builder, builder.GetObject(
"TabsView").Handle);
550 Menu recent =
new Menu();
552 protected TabsView(Builder builder, IntPtr handle) : base(handle)
556 builder.Autoconnect(
this);
557 InitializeOpenRecentMenu();
558 filteredMenu.Active =
true;
560 progress =
Progress.Create(title,
"",
"");
563 new Gtk.FileChooserDialog(
"Choose file to open",
565 FileChooserAction.Open,
566 "Cancel", ResponseType.Cancel,
567 "OK", ResponseType.Accept);
568 filechooser.SelectMultiple =
true;
570 foreach (RotateFlipType flip
in (RotateFlipType[])Enum.GetValues(typeof(RotateFlipType)))
572 MenuItem mi =
new MenuItem(flip.ToString());
573 mi.ButtonPressEvent += RotateFlip_ButtonPressEvent;
576 rotateFlipMenu.Submenu = m;
577 rotateFlipMenu.ShowAll();
578 string a =
"ABCDEFGHIJKLMNOPQRSTUVWXYZ";
579 Menu me =
new Menu();
580 foreach (
char c
in a)
582 MenuItem mi =
new MenuItem(c.ToString());
583 Menu men =
new Menu();
584 foreach (Fiji.Macro.Command command in Fiji.Macro.Commands.Values)
586 if (command.Name.StartsWith(c))
588 MenuItem menuItem =
new MenuItem(command.Name.ToString());
589 menuItem.ButtonPressEvent += CommandMenuItem_ButtonPressEvent;
590 men.Append(menuItem);
596 commandsMenu.Submenu = me;
597 commandsMenu.ShowAll();
598 recentMenu.Submenu = recent;
600 App.ApplyStyles(
this);
601 Menu rm =
new Menu();
602 foreach(Scripting.Script s in Scripting.scripts.Values)
604 MenuItem mi =
new MenuItem(s.name);
605 mi.ButtonPressEvent += Mi_ButtonPressEvent;
609 Plugins.Initialize();
612 App.MoveToMain(
this);
636 string st =
BioLib.Settings.GetSettings(
"ImageJPath");
639 Fiji.ImageJPath =
BioLib.Settings.GetSettings(
"ImageJPath");
642 string title =
"Select ImageJ Executable Location";
643 if (OperatingSystem.IsMacOS())
644 title =
"Select ImageJ Executable Location (Fiji.app/Contents/MacOS/ImageJ-macosx)";
645 Gtk.FileChooserDialog filechooser =
646 new Gtk.FileChooserDialog(title,
Scripting.window,
647 FileChooserAction.Open,
648 "Cancel", ResponseType.Cancel,
649 "Save", ResponseType.Accept);
650 filechooser.SetCurrentFolder(System.IO.Path.GetDirectoryName(Environment.ProcessPath));
651 if (filechooser.Run() != (
int)ResponseType.Accept)
653 Fiji.ImageJPath = filechooser.Filename;
654 BioLib.Settings.AddSettings(
"ImageJPath", filechooser.Filename);
655 filechooser.Destroy();
659 private void Mi_ButtonPressEvent(
object o, ButtonPressEventArgs args)
661 MenuItem m = (MenuItem)o;
662 if (m.Label.EndsWith(
".ijm") || m.Label.EndsWith(
".txt") && !m.Label.EndsWith(
".cs"))
664 string file = Fiji.ImageJPath + m.Label;
665 string ma = File.ReadAllText(file);
669 Scripting.RunByName(m.Label);
671 foreach (MenuItem item
in recent.Children)
673 if (item.Label == m.Label)
678 recentMenu.ShowAll();
681 private async
void CommandMenuItem_ButtonPressEvent(
object o, ButtonPressEventArgs args)
683 if (ImageView.SelectedImage ==
null)
return;
684 MenuItem m = (MenuItem)o;
685 BioImage sourceImage = ImageView.SelectedImage;
687 string command =
"run(\"" + m.Label +
"\");";
688 if (ImageView.SelectedImage.isPyramidal)
690 YesNoDialog pd = YesNoDialog.Create(
"Run on all pyramidal levels?");
691 if (pd.Run() == (
int)ResponseType.Yes)
693 StartProgress(
"Run Command",
"Running " + m.Label +
" on all pyramid levels.");
696 bm = await ImageJ.RunOnAllPyramidLevels(ImageView.SelectedImage, command, BioConsole.headless, BioConsole.onTab, BioConsole.useBioformats, BioConsole.resultInNewTab);
702 AddToRecent(m.Label);
706 ComboPicker cpd = ComboPicker.Create(
"Select pyramidal level to run command on.", ImageView.SelectedImage.Resolutions.Select(l => l.ToString()).ToArray());
707 if(cpd.Run() == (
int)ResponseType.Ok)
709 int level = cpd.SelectedIndex;
710 bool currentViewOnly =
false;
711 if (IsRemoteZarrSource(ImageView.SelectedImage))
713 YesNoDialog speedDialog = YesNoDialog.Create(
"Use only the current visible region for speed?");
714 currentViewOnly = speedDialog.Run() == (int)ResponseType.Yes;
717 string scopeText = currentViewOnly
718 ?
"the current visible region of pyramid level " + (level + 1).ToString()
719 :
"pyramid level " + (level + 1).ToString();
720 StartProgress(
"Run Command",
"Running " + m.Label +
" on " + scopeText +
".");
723 bm = await ImageJ.RunOnPyramidalImage(ImageView.SelectedImage, command, level, BioConsole.headless, BioConsole.onTab, BioConsole.useBioformats, BioConsole.resultInNewTab, currentViewOnly);
729 AddToRecent(m.Label);
736 Console.WriteLine($
"[TabsView.CommandMenuItem] pyramidal result={(bm == null ? "null" : bm.ID)}");
737 PreserveSourceAnnotations(sourceImage, bm);
741 bm = await Fiji.RunOnImage(ImageView.SelectedImage,
"run(\"" + m.Label +
"\");", BioConsole.headless, BioConsole.onTab, BioConsole.useBioformats, BioConsole.resultInNewTab);
742 Console.WriteLine($
"[TabsView.CommandMenuItem] stack result={(bm == null ? "null" : bm.ID)}");
743 AddToRecent(m.Label);
744 PreserveSourceAnnotations(sourceImage, bm);
748 private static void PreserveSourceAnnotations(BioImage source, BioImage target)
750 if (source ==
null || target ==
null)
753 if (target.Annotations ==
null)
754 target.Annotations =
new List<ROI>();
756 foreach (ROI roi
in source.Annotations ?? Enumerable.Empty<ROI>())
761 ROI copy = roi.Copy();
762 ScaleAnnotationToTargetVolume(copy, source, target);
763 target.Annotations.Add(copy);
766 if (App.viewer ==
null)
769 foreach (ROI roi
in App.viewer.GetZarrLabelOverlaysForImage(source))
774 if (!
string.IsNullOrWhiteSpace(roi.roiID) &&
775 target.Annotations.Any(a => a !=
null && a.roiID == roi.roiID))
778 ROI copy = roi.Copy();
779 ScaleAnnotationToTargetVolume(copy, source, target);
780 target.Annotations.Add(copy);
784 private static void ScaleAnnotationToTargetVolume(ROI roi, BioImage source, BioImage target)
786 if (roi ==
null || source ==
null || target ==
null)
789 double sourceW = GetStageWidth(source);
790 double sourceH = GetStageHeight(source);
791 double targetW = GetStageWidth(target);
792 double targetH = GetStageHeight(target);
794 if (sourceW <= 0 || sourceH <= 0 || targetW <= 0 || targetH <= 0)
797 double scaleX = targetW / sourceW;
798 double scaleY = targetH / sourceH;
799 if (Math.Abs(scaleX - 1.0) < 0.000001 && Math.Abs(scaleY - 1.0) < 0.000001)
802 double MapX(
double x) => target.Volume.Location.X + ((x - source.Volume.Location.X) * scaleX);
803 double MapY(
double y) => target.Volume.Location.Y + ((y - source.Volume.Location.Y) * scaleY);
805 roi.BoundingBox =
new AForge.RectangleD(
806 MapX(roi.BoundingBox.X),
807 MapY(roi.BoundingBox.Y),
808 roi.BoundingBox.W * scaleX,
809 roi.BoundingBox.H * scaleY);
811 for (
int i = 0; i < roi.Points.Count; i++)
812 roi.Points[i] =
new AForge.PointD(MapX(roi.Points[i].X), MapY(roi.Points[i].Y));
814 if (roi.roiMask !=
null)
816 double maskStageX = roi.roiMask.X * roi.roiMask.PhysicalSizeX;
817 double maskStageY = roi.roiMask.Y * roi.roiMask.PhysicalSizeY;
818 roi.roiMask.PhysicalSizeX *= scaleX;
819 roi.roiMask.PhysicalSizeY *= scaleY;
820 roi.roiMask.X = MapX(maskStageX) / Math.Max(roi.roiMask.PhysicalSizeX, 1e-12);
821 roi.roiMask.Y = MapY(maskStageY) / Math.Max(roi.roiMask.PhysicalSizeY, 1e-12);
825 private static double GetStageWidth(BioImage image)
830 if (image.Resolutions !=
null && image.Resolutions.Count > 0 &&
831 image.Resolutions[0].SizeX > 0 && image.Resolutions[0].PhysicalSizeX > 0)
832 return image.Resolutions[0].SizeX * image.Resolutions[0].PhysicalSizeX;
834 return image.SizeX * (image.PhysicalSizeX > 0 ? image.PhysicalSizeX : 1.0);
837 private static double GetStageHeight(BioImage image)
842 if (image.Resolutions !=
null && image.Resolutions.Count > 0 &&
843 image.Resolutions[0].SizeY > 0 && image.Resolutions[0].PhysicalSizeY > 0)
844 return image.Resolutions[0].SizeY * image.Resolutions[0].PhysicalSizeY;
846 return image.SizeY * (image.PhysicalSizeY > 0 ? image.PhysicalSizeY : 1.0);
849 private static void NormalizeMasksForOmeExport(BioImage image)
851 if (image ==
null || image.Annotations ==
null || image.Annotations.Count == 0)
854 foreach (ROI roi
in image.Annotations)
856 if (roi?.roiMask ==
null)
859 if (roi.roiMask.PhysicalSizeX <= 0)
860 roi.roiMask.PhysicalSizeX = image.PhysicalSizeX > 0 ? image.PhysicalSizeX : 1.0;
862 if (roi.roiMask.PhysicalSizeY <= 0)
863 roi.roiMask.PhysicalSizeY = image.PhysicalSizeY > 0 ? image.PhysicalSizeY : 1.0;
867 private void InitializeOpenRecentMenu()
869 openRecentMenu =
new MenuItem(
"Open Recent");
870 openRecentMenu.Submenu = recentImagesMenu;
871 clearRecentImagesMenu =
new MenuItem(
"Clear Recent Files");
872 clearRecentImagesMenu.Activated += ClearRecentImagesMenu_Activated;
873 Menu fileMenu = (Menu)openImagesMenu.Parent;
874 int recentMenuPosition = Array.IndexOf(fileMenu.Children, openImagesMenu) + 1;
875 fileMenu.Insert(openRecentMenu, recentMenuPosition);
876 fileMenu.Insert(clearRecentImagesMenu, recentMenuPosition + 1);
880 if (File.Exists(RecentImagesPath))
882 var savedImages = JsonSerializer.Deserialize<List<string>>(File.ReadAllText(RecentImagesPath));
883 if (savedImages !=
null)
885 recentImages.AddRange(savedImages
886 .Where(source => !
string.IsNullOrWhiteSpace(source))
887 .Distinct(StringComparer.Ordinal)
888 .Take(MaxRecentImages));
892 catch (Exception ex) when (ex is IOException || ex is UnauthorizedAccessException || ex is JsonException)
894 Console.WriteLine(
"Could not load recent images: " + ex.Message);
897 RefreshOpenRecentMenu();
900 private void ClearRecentImagesMenu_Activated(
object sender, EventArgs args)
902 recentImages.Clear();
905 if (File.Exists(RecentImagesPath))
906 File.Delete(RecentImagesPath);
908 catch (Exception ex) when (ex is IOException || ex is UnauthorizedAccessException)
910 Console.WriteLine(
"Could not clear recent images: " + ex.Message);
913 RefreshOpenRecentMenu();
916 private void RememberRecentImage(
string source)
918 if (
string.IsNullOrWhiteSpace(source))
921 bool remote = source.StartsWith(
"https://", StringComparison.OrdinalIgnoreCase) ||
922 source.StartsWith(
"http://", StringComparison.OrdinalIgnoreCase) ||
923 source.StartsWith(
"s3://", StringComparison.OrdinalIgnoreCase);
925 if (!remote && !File.Exists(source) && !Directory.Exists(source))
928 source = System.IO.Path.GetFullPath(source);
930 StringComparison comparison = !remote && OperatingSystem.IsWindows()
931 ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal;
932 recentImages.RemoveAll(item =>
string.Equals(item, source, comparison));
933 recentImages.Insert(0, source);
934 if (recentImages.Count > MaxRecentImages)
935 recentImages.RemoveRange(MaxRecentImages, recentImages.Count - MaxRecentImages);
939 Directory.CreateDirectory(System.IO.Path.GetDirectoryName(RecentImagesPath));
940 File.WriteAllText(RecentImagesPath, JsonSerializer.Serialize(recentImages));
942 catch (Exception ex) when (ex is IOException || ex is UnauthorizedAccessException)
944 Console.WriteLine(
"Could not save recent images: " + ex.Message);
947 RefreshOpenRecentMenu();
950 private void RefreshOpenRecentMenu()
952 foreach (Widget item
in recentImagesMenu.Children)
954 recentImagesMenu.Remove(item);
958 foreach (
string source
in recentImages)
960 var item =
new MenuItem(source) { UseUnderline =
false };
961 item.Activated += async (sender, args) => await OpenRecentImageAsync(source);
962 recentImagesMenu.Append(item);
965 openRecentMenu.Sensitive = recentImages.Count > 0;
966 clearRecentImagesMenu.Sensitive = recentImages.Count > 0;
967 openRecentMenu.ShowAll();
968 clearRecentImagesMenu.ShowAll();
971 private async Task OpenRecentImageAsync(
string source)
973 StartProgress(
"Open Recent",
"Opening");
976 await FlushGtkEventsAsync();
977 await OpenPyramidRootsAsTabsAsync(source);
981 using var dialog =
new MessageDialog(
this, DialogFlags.Modal,
982 MessageType.Error, ButtonsType.Ok,
"{0}", ex.Message);
983 dialog.Title =
"Open Recent Failed";
993 void AddToRecent(
string name)
996 foreach (MenuItem item
in recent.Children)
998 if (item.Label == name)
1004 MenuItem mi =
new MenuItem(name);
1007 mi.ButtonPressEvent += CommandMenuItem_ButtonPressEvent;
1008 recentMenu.ShowAll();
1018 private void RotateFlip_ButtonPressEvent(
object o, ButtonPressEventArgs args)
1020 if(ImageView.SelectedImage==
null)
return;
1021 MenuItem m = (MenuItem)o;
1022 RotateFlipType r = Enum.Parse<RotateFlipType>(m.Label);
1023 ImageView.SelectedImage.RotateFlip(r);
1024 App.viewer.UpdateImage();
1025 App.viewer.UpdateView();
1035 openURLMenu.ButtonPressEvent += OpenURLMenu_ButtonPressEvent;
1040 openQuPathProject.ButtonPressEvent += openQuPathProjectMenuClick;
1043 openZarr.ButtonPressEvent += OpenZarr_ButtonPressEvent;
1046 saveNumPyBut.ButtonPressEvent += SaveNumPyBut_ButtonPressEvent;
1051 savePyramidalMenu.ButtonPressEvent += SavePyramidalMenu_ButtonPressEvent;
1052 saveZarr.ButtonPressEvent += SaveZarr_ButtonPressEvent;
1053 saveQuPathProject.ButtonPressEvent += saveQuPathProject_ButtonPressEvent;
1055 uploadImageMenu.ButtonPressEvent += UploadImageMenu_ButtonPressEvent;
1057 rgbMenu.ButtonPressEvent += RgbMenu_ButtonPressEvent;
1058 filteredMenu.ButtonPressEvent += FilteredMenu_ButtonPressEvent;
1059 rawMenu.ButtonPressEvent += RawMenu_ButtonPressEvent;
1060 emissionMenu.ButtonPressEvent += EmissionMenu_ButtonPressEvent;
1064 runSAMMenu.ButtonPressEvent += RunSAMMenu_ButtonPressEvent;
1065 runMicroSAMMenu.ButtonPressEvent += RunMicroSAMMenu_ButtonPressEvent;
1066 plateToolMenu.ButtonPressEvent += PlateToolMenu_ButtonPressEvent;
1067 extractRegionMenu.ButtonPressEvent += ExtractRegionMenu_ButtonPressEvent;
1073 importROIsFromImageJMenu.ButtonPressEvent += ImportROIsFromImageJMenu_ButtonPressEvent;
1074 exportROIsFromImageJMenu.ButtonPressEvent += ExportROIsFromImageJMenu_ButtonPressEvent;
1075 importROIQuPath.ButtonPressEvent += ImportROIQuPath_ButtonPressEvent;
1076 exportROIQuPath.ButtonPressEvent += ExportROIQuPath_ButtonPressEvent;
1077 importPointsNapari.ButtonPressEvent += ImportPointsNapari_ButtonPressEvent;
1078 importShapesNapari.ButtonPressEvent += ImportShapesNapari_ButtonPressEvent;
1079 exportROINapari.ButtonPressEvent += ExportROINapari_ButtonPressEvent;
1086 focusMenu.ButtonPressEvent += FocusMenu_ButtonPressEvent;
1093 toFloatMenu.ButtonPressEvent += ToFloatMenu_ButtonPressEvent;
1094 toShortMenu.ButtonPressEvent += ToShortMenu_ButtonPressEvent;
1100 scriptRecorderMenu.ButtonPressEvent += ScriptRecorderMenu_ButtonPressEvent;
1104 tabClose.ButtonPressEvent += TabClose_ButtonPressEvent;
1105 tabDuplicate.ButtonPressEvent += TabDuplicate_ButtonPressEvent;
1106 this.FocusInEvent += TabsView_FocusInEvent;
1107 rgbMenu.ButtonPressEvent += RgbMenu_ButtonPressEvent;
1108 filteredMenu.ButtonPressEvent += FilteredMenu_ButtonPressEvent;
1109 rawMenu.ButtonPressEvent += RawMenu_ButtonPressEvent;
1110 emissionMenu.ButtonPressEvent += EmissionMenu_ButtonPressEvent;
1111 tabsView.SwitchPage += TabsView_SwitchPage;
1112 tabsView.ButtonPressEvent += TabsView_ButtonPressEvent;
1113 this.WindowStateEvent += TabsView_WindowStateEvent;
1114 this.DeleteEvent += TabsView_DeleteEvent;
1115 omeroMenu.ButtonPressEvent += OmeroMenu_ButtonPressEvent;
1117 searchMenu.ButtonPressEvent += SearchMenu_ButtonPressEvent;
1118 updateMenu.ButtonPressEvent += UpdateMenu_ButtonPressEvent;
1119 renameMenu.ButtonPressEvent += RenameMenu_ButtonPressEvent;
1120 AIMenu.ButtonPressEvent += AiMenu_ButtonPressEvent;
1123 Gtk.Drag.DestSet(
this, DestDefaults.All,
new TargetEntry[]
1125 new TargetEntry(
"text/uri-list", TargetFlags.OtherApp, 0)
1126 }, Gdk.DragAction.Copy);
1127 this.DragDataReceived += TabsView_DragDataReceived;
1130 private async
void SaveZarr_ButtonPressEvent(
object o, ButtonPressEventArgs args)
1132 Gtk.FileChooserDialog filechooser =
1133 new Gtk.FileChooserDialog(
"Choose Zarr file to save.",
1135 FileChooserAction.Save,
1136 "Cancel", ResponseType.Cancel,
1137 "Open", ResponseType.Accept);
1138 if (filechooser.Run() != (
int)ResponseType.Accept)
1141 StartProgress(
"Saving Zarr",
"Saving " +
ImageView.SelectedImage.ID +
" as Zarr file.");
1142 BioImage.Progress = 0;
1145 ImageView.SelectedImage = await BioImage.OpenFile(filechooser.Filename);
1147 await Task.Run(() =>
1149 Zarr.SaveZarr(
ImageView.SelectedImage, filechooser.Filename);
1150 Zarr.SaveV2Compatibility(
ImageView.SelectedImage, filechooser.Filename);
1151 MergeZarrLabelOverlaysIntoAnnotations(
ImageView.SelectedImage);
1152 TrySaveZarrROIs(
ImageView.SelectedImage, filechooser.Filename);
1153 TrySaveZarrLabelOverlays(
ImageView.SelectedImage, filechooser.Filename);
1158 private async
void OpenZarr_ButtonPressEvent(
object o, ButtonPressEventArgs args)
1160 Gtk.FileChooserDialog filechooser =
1161 new Gtk.FileChooserDialog(
"Choose Zarr file to open.",
1163 FileChooserAction.SelectFolder,
1164 "Cancel", ResponseType.Cancel,
1165 "Open", ResponseType.Accept);
1166 if (filechooser.Run() != (
int)ResponseType.Accept)
1170 string[] sts = filechooser.Filenames;
1171 StartProgressHidden(
"Open Zarr",
"Opening");
1175 foreach (
string f
in filechooser.Filenames)
1177 await OpenPyramidRootsAsViewsAsync(f,
true);
1186 private async
void OpenURLMenu_ButtonPressEvent(
object o, ButtonPressEventArgs args)
1188 OpenUrlBox box = OpenUrlBox.Create();
1191 if (box.Run() == (
int)ResponseType.Ok)
1193 string url = box.GetUrl();
1194 StartProgress(
"Open URL",
"Opening");
1198 await OpenPyramidRootsAsViewsAsync(url,
true);
1200 catch (Exception ex)
1202 var dialog =
new MessageDialog(
1208 dialog.Title =
"Open URL Failed";
1224 private void ImportPointsNapari_ButtonPressEvent(
object o, ButtonPressEventArgs args)
1226 Gtk.FileChooserDialog filechooser =
1227 new Gtk.FileChooserDialog(
"Choose Napari Points files to import.",
1229 FileChooserAction.Open,
1230 "Cancel", ResponseType.Cancel,
1231 "Open", ResponseType.Accept);
1232 filechooser.SelectMultiple =
true;
1233 if (filechooser.Run() != (
int)ResponseType.Accept)
1235 string[] sts = filechooser.Filenames;
1236 Napari.Initialize(ImageView.SelectedImage.PhysicalSizeX, ImageView.SelectedImage.PhysicalSizeY,ImageView.SelectedImage.SizeT > 1);
1237 foreach (var f
in filechooser.Filenames)
1239 if (f.EndsWith(
".CSV") || f.EndsWith(
".csv"))
1240 ImageView.SelectedImage.Annotations.AddRange(Napari.ParsePointsFile(f));
1242 filechooser.Destroy();
1243 BioLib.Recorder.Record(
"Napari.ParsePointsFile(" + StrArray(sts) +
");");
1246 private void ExportROINapari_ButtonPressEvent(
object o, ButtonPressEventArgs args)
1248 Gtk.FileChooserDialog filechooser =
1249 new Gtk.FileChooserDialog(
"Choose filename for Shape and Points CSV files to save to.",
1251 FileChooserAction.Save,
1252 "Cancel", ResponseType.Cancel,
1253 "Open", ResponseType.Accept);
1254 if (filechooser.Run() != (
int)ResponseType.Accept)
1256 string file = filechooser.Filename;
1257 Napari.Initialize(ImageView.SelectedImage.PhysicalSizeX, ImageView.SelectedImage.PhysicalSizeY, ImageView.SelectedImage.SizeT > 1);
1258 Napari.WriteNapariFiles(file +
"_points.csv", file +
"_shapes.csv", ImageView.SelectedImage.Annotations);
1259 filechooser.Destroy();
1260 BioLib.Recorder.Record(
"Napari.WriteNapariFiles(" + Q(file +
"_points.csv") +
", " + Q(file +
"_shapes.csv") +
", ImageView.SelectedImage.Annotations);");
1263 private void ImportShapesNapari_ButtonPressEvent(
object o, ButtonPressEventArgs args)
1265 Gtk.FileChooserDialog filechooser =
1266 new Gtk.FileChooserDialog(
"Choose Napari Shape files to import.",
1268 FileChooserAction.Open,
1269 "Cancel", ResponseType.Cancel,
1270 "Open", ResponseType.Accept);
1271 filechooser.SelectMultiple =
true;
1272 if (filechooser.Run() != (
int)ResponseType.Accept)
1274 Napari.Initialize(ImageView.SelectedImage.PhysicalSizeX, ImageView.SelectedImage.PhysicalSizeY, ImageView.SelectedImage.SizeT > 1);
1275 foreach (var f
in filechooser.Filenames)
1277 if (f.EndsWith(
".CSV") || f.EndsWith(
".csv"))
1279 List<ROI> rs = Napari.ParseShapesFile(f);
1280 ImageView.SelectedImage.Annotations.AddRange(rs);
1283 filechooser.Destroy();
1286 private void UploadImageMenu_ButtonPressEvent(
object o, ButtonPressEventArgs args)
1288 if (
BioLib.OMERO.sc ==
null)
1289 OmeroMenu_ButtonPressEvent(o, args);
1290 BioImage b = ImageView.SelectedImage;
1291 var user =
BioLib.OMERO.gateway.getLoggedInUser();
1292 long userId = user.getId();
1296 imgid =
long.Parse(b.ID);
1300 Console.WriteLine(e.Message);
1302 BioLib.Recorder.Record(
"OMERO.Upload(" + (b?.ID ??
string.Empty) +
", " + userId +
");");
1305 BioLib.OMERO.Upload(b, userId);
1309 imgid =
long.Parse(b.ID);
1313 Console.WriteLine(e.Message);
1317 private void AiMenu_ButtonPressEvent(
object o, ButtonPressEventArgs args)
1320 BioLib.Recorder.Record(
"AI.Create();");
1323 private async
void TabsView_DragDataReceived(
object o, DragDataReceivedArgs args)
1326 string receivedData = System.Text.Encoding.UTF8.GetString(args.SelectionData.Data);
1329 string[] uris = receivedData.Split(
new[] {
'\r',
'\n' }, StringSplitOptions.RemoveEmptyEntries);
1330 foreach (
string uri
in uris)
1332 if (Uri.TryCreate(uri, UriKind.Absolute, out Uri fileUri) && fileUri.IsFile)
1334 string filePath = fileUri.LocalPath;
1335 await OpenPyramidRootsAsTabsAsync(filePath);
1342 private void OmeroMenu_ButtonPressEvent(
object o, ButtonPressEventArgs args)
1344 Login log = Login.Create();
1345 int status = log.Run();
1346 if (status == (
int)ResponseType.Ok)
1348 OMERO omr = OMERO.Create();
1351 BioLib.Recorder.Record(
"Login.Create();");
1354 private void SaveNumPyBut_ButtonPressEvent(
object o, ButtonPressEventArgs args)
1356 Gtk.FileChooserDialog filechooser =
1357 new Gtk.FileChooserDialog(
"Choose filename for NumPy file to save to.",
1359 FileChooserAction.Save,
1360 "Cancel", ResponseType.Cancel,
1361 "Open", ResponseType.Accept);
1362 filechooser.SelectMultiple =
false;
1363 if (filechooser.Run() != (
int)ResponseType.Accept)
1365 NumPy.SaveNumPy(ImageView.SelectedImage,filechooser.Filename);
1367 BioLib.Recorder.Record(
"NumPy.SaveNumPy(ImageView.SelectedImage, " + Q(filechooser.Filename) +
");");
1370 private void RunMicroSAMMenu_ButtonPressEvent(
object o, ButtonPressEventArgs args)
1372 SAMTool sam = SAMTool.Create(
true);
1376 BioLib.Recorder.Record(
"SAMTool.Create(true);");
1379 public void RenameTab(
string name,
string newName)
1382 foreach (Widget item
in tabsView.Children)
1385 Label l = (Label)tabsView.GetTabLabel(tabsView.GetNthPage(i));
1389 BioLib.Recorder.Record(
"TabsView.RenameTab(" + Q(name) +
", " + Q(newName) +
");");
1395 private void TabsView_FocusInEvent(
object o, FocusInEventArgs args)
1397 App.tabsView =
this;
1400 private void RenameMenu_ButtonPressEvent(
object o, ButtonPressEventArgs args)
1402 TextInput ti = TextInput.Create();
1404 if ((
int)ti.Run() == (
int)ResponseType.Ok)
1406 App.Rename(ti.Text);
1408 BioLib.Recorder.Record(
"App.Rename(" + Q(ti.Text) +
");");
1411 private void TabsView_DeleteEvent(
object o, DeleteEventArgs args)
1413 foreach (var v
in viewers)
1415 foreach (var b
in v.Images)
1418 Images.RemoveImage(b);
1424 private async
void ExtractRegionMenu_ButtonPressEvent(
object o, ButtonPressEventArgs args)
1426 BioImage b = ImageView.SelectedImage.Copy();
1427 String name = System.IO.Path.GetFileNameWithoutExtension(ImageView.SelectedImage.Filename).Replace(
".ome",
"");
1428 int c = Images.GetImageCountByName(name);
1429 b.ID = name +
"-" + c +
".ome.tif";
1431 if(b.OpenSlideBase !=
null)
1433 bms = await b.OpenSlideBase.GetSliceAsync(
new OpenSlideGTK.SliceInfo(b.PyramidalOrigin.X, b.PyramidalOrigin.Y, b.PyramidalSize.Width, b.PyramidalSize.Height, b.Resolution),b.Level,b.Coordinate);
1436 bms = await b.SlideBase.GetSlice(
1437 new SliceInfo(b.PyramidalOrigin.X, b.PyramidalOrigin.Y, b.PyramidalSize.Width, b.PyramidalSize.Height, b.Resolution, b.Coordinate),
1438 b.PyramidalOrigin, b.PyramidalSize);
1439 Point3D loc = b.Volume.Location;
1440 Point3D p =
new Point3D(b.StageSizeX + b.PyramidalOrigin.X, b.StageSizeY + b.PyramidalOrigin.Y, b.StageSizeZ + b.SizeZ);
1441 Resolution res =
new Resolution(b.PyramidalSize.Width, b.PyramidalSize.Height, b.Buffers[0].PixelFormat,b.PhysicalSizeX,b.PhysicalSizeY,b.PhysicalSizeZ,p.X,p.Y,p.Z);
1442 b.Resolutions.Clear();
1443 b.Resolutions.Add(res);
1445 b.Buffers.Add(
new Bitmap((
int)b.PyramidalOrigin.X, (
int)b.PyramidalOrigin.Y,PixelFormat.Format32bppArgb));
1450 private void ToShortMenu_ButtonPressEvent(
object o, ButtonPressEventArgs args)
1452 ImageView.SelectedImage.ToShort();
1455 private void ToFloatMenu_ButtonPressEvent(
object o, ButtonPressEventArgs args)
1457 ImageView.SelectedImage.ToFloat();
1460 private void UpdateMenu_ButtonPressEvent(
object o, ButtonPressEventArgs args)
1465 private void PlateToolMenu_ButtonPressEvent(
object o, ButtonPressEventArgs args)
1467 PlateTool pl = PlateTool.Create();
1468 BioLib.Recorder.Record(
"App.viewer.Mode = ImageView.ViewMode.Emission;");
1471 private void ExportROIQuPath_ButtonPressEvent(
object o, ButtonPressEventArgs args)
1473 Gtk.FileChooserDialog filechooser =
1474 new Gtk.FileChooserDialog(
"Choose QuPath ROI(.geojson) file to save to.",
1476 FileChooserAction.Save,
1477 "Cancel", ResponseType.Cancel,
1478 "Open", ResponseType.Accept);
1479 filechooser.SelectMultiple =
true;
1480 if (filechooser.Run() != (
int)ResponseType.Accept)
1482 QuPath.SaveROI(filechooser.Filename, ImageView.SelectedImage);
1484 BioLib.Recorder.Record(
"BioImage.ExportROIsCSV(" + Q(filechooser.Filename) +
", ImageView.SelectedImage.Annotations);");
1487 private void ImportROIQuPath_ButtonPressEvent(
object o, ButtonPressEventArgs args)
1489 Gtk.FileChooserDialog filechooser =
1490 new Gtk.FileChooserDialog(
"Choose QuPath ROI(.geojson) file to open",
1492 FileChooserAction.Open,
1493 "Cancel", ResponseType.Cancel,
1494 "Open", ResponseType.Accept);
1495 filechooser.SelectMultiple =
true;
1496 if (filechooser.Run() != (
int)ResponseType.Accept)
1498 foreach (
string item
in filechooser.Filenames)
1500 ROI[] rois = QuPath.ReadROI(item,ImageView.SelectedImage);
1501 foreach (ROI r
in rois)
1503 ImageView.SelectedImage.Annotations.Add(r);
1507 BioLib.Recorder.Record(
"QuPath.ReadROI(" + StrArray(filechooser.Filenames) +
", ImageView.SelectedImage);");
1510 private void SearchMenu_ButtonPressEvent(
object o, ButtonPressEventArgs args)
1512 Search search = Search.Create();
1514 BioLib.Recorder.Record(
"App.viewer.Mode = ImageView.ViewMode.Raw;");
1517 private void TabsView_ButtonPressEvent(
object o, ButtonPressEventArgs args)
1519 contextMenu.Popup();
1522 private void TabDuplicate_ButtonPressEvent(
object o, ButtonPressEventArgs args)
1524 List<BioImage> bm =
new List<BioImage>();
1525 foreach (BioImage item
in App.viewer.Images)
1527 bm.Add(item.Copy());
1529 for (
int i = 1; i < bm.Count; i++)
1531 Images.AddImage(bm[i]);
1534 BioLib.Recorder.Record(
"App.viewer.Images.Count;");
1537 private void TabClose_ButtonPressEvent(
object o, ButtonPressEventArgs args)
1539 if (App.viewer ==
null)
return;
1540 ImageView v = App.viewer;
1541 foreach (BioImage item
in v.Images)
1543 TrySaveZarrROIs(item);
1544 Images.RemoveImage(item);
1551 BioLib.Recorder.Record(
"App.viewer.Images.Count;");
1554 private void RunSAMMenu_ButtonPressEvent(
object o, ButtonPressEventArgs args)
1556 SAMTool sam = SAMTool.Create(
false);
1562 private async
void SavePyramidalMenu_ButtonPressEvent(
object o, ButtonPressEventArgs args)
1564 Gtk.FileChooserDialog filechooser =
1565 new Gtk.FileChooserDialog(
"Set Pyramidal filename to save",
1567 FileChooserAction.Save,
1568 "Cancel", ResponseType.Cancel,
1569 "Save", ResponseType.Accept);
1570 if (filechooser.Run() != (
int)ResponseType.Accept)
1573 ComboPicker picker = ComboPicker.Create(typeof(NetVips.Enums.ForeignTiffCompression));
1574 picker.Title =
"Select Compression";
1576 if(picker.Run() != (
int)ResponseType.Ok)
1578 NumberPicker numpicker = NumberPicker.Create(0,100,0);
1579 numpicker.Title =
"Select Compression Level in %";
1580 if (numpicker.Run() != (
int)ResponseType.Ok)
1582 await BioImage.SavePyramidalAsync(App.viewer.Images.ToArray(), filechooser.Filename, (NetVips.Enums.ForeignTiffCompression)picker.SelectedIndex, (
int)numpicker.SelectedValue);
1583 BioLib.Recorder.Record(
"BioImage.SavePyramidalAsync(App.viewer.Images.ToArray(), " + Q(filechooser.Filename) +
", (NetVips.Enums.ForeignTiffCompression)" + picker.SelectedIndex +
", " + (
int)numpicker.SelectedValue +
");");
1586 private void FocusMenu_ButtonPressEvent(
object o, ButtonPressEventArgs args)
1588 if (ImageView.SelectedImage ==
null)
return;
1589 ZCT co = App.viewer.GetCoordinate();
1590 int f = BioImage.FindFocus(ImageView.SelectedImage, co.C, co.T);
1591 ZCT z = ImageView.SelectedImage.Buffers[f].Coordinate;
1592 App.viewer.SetCoordinate(z.Z, z.C, z.T);
1600 private void ScriptRecorderMenu_ButtonPressEvent(
object o, ButtonPressEventArgs args)
1602 App.recorder = Recorder.Create();
1603 App.recorder.Show();
1604 App.recorder.Present();
1605 BioLib.Recorder.Record(
"App.viewer.Mode = ImageView.ViewMode.Filtered;");
1614 private void ExportROIsFromImageJMenu_ButtonPressEvent(
object o, ButtonPressEventArgs args)
1616 Gtk.FileChooserDialog filechooser =
1617 new Gtk.FileChooserDialog(
"Set ROI filename to save",
1619 FileChooserAction.Save,
1620 "Cancel", ResponseType.Cancel,
1621 "Save", ResponseType.Accept);
1622 if (filechooser.Run() != (
int)ResponseType.Accept)
1625 foreach (ROI item
in ImageView.SelectedImage.Annotations)
1627 string f = filechooser.Filename;
1628 if (!f.EndsWith(
".roi"))
1632 f = f.Replace(
".roi",
"") + i +
".roi";
1634 Fiji.RoiEncoder.save(ImageView.SelectedImage, item, f);
1638 BioLib.Recorder.Record(
"Fiji.RoiEncoder.save(ImageView.SelectedImage, item, " + Q(filechooser.Filename) +
");");
1648 private void ImportROIsFromImageJMenu_ButtonPressEvent(
object o, ButtonPressEventArgs args)
1650 Gtk.FileChooserDialog filechooser =
1651 new Gtk.FileChooserDialog(
"Choose ROI file to open",
1653 FileChooserAction.Open,
1654 "Cancel", ResponseType.Cancel,
1655 "Open", ResponseType.Accept);
1656 filechooser.SelectMultiple =
true;
1657 if (filechooser.Run() != (
int)ResponseType.Accept)
1659 foreach (
string item
in filechooser.Filenames)
1661 ROI roi = Fiji.RoiDecoder.open(item, ImageView.SelectedImage.PhysicalSizeX, ImageView.SelectedImage.PhysicalSizeY, ImageView.SelectedImage.Volume.Location.X, ImageView.SelectedImage.Volume.Location.Y);
1662 ImageView.SelectedImage.Annotations.Add(roi);
1665 BioLib.Recorder.Record(
"Fiji.RoiDecoder.open(" + StrArray(filechooser.Filenames) +
", ImageView.SelectedImage.PhysicalSizeX, ImageView.SelectedImage.PhysicalSizeY, ImageView.SelectedImage.Volume.Location.X, ImageView.SelectedImage.Volume.Location.Y);");
1674 private void TabsView_WindowStateEvent(
object o, WindowStateEventArgs args)
1676 if ((args.Event.ChangedMask & Gdk.WindowState.Iconified) != 0)
1678 foreach (ImageView v
in viewers)
1684 else if ((args.Event.ChangedMask & Gdk.WindowState.Maximized) == 0 && (args.Event.ChangedMask & Gdk.WindowState.Iconified) == 0)
1686 foreach (ImageView v
in viewers)
1691 if(!v.Visible && v.Images.Count > 0)
1702 private void TabsView_SwitchPage(
object o, SwitchPageArgs args)
1704 if(viewers.Count == 0)
return;
1705 viewers[(int)args.PageNum].Present();
1706 App.viewer = viewers[(int)args.PageNum];
1707 ImageView.SelectedImage = App.viewer.Images[0];
1708 App.roiManager?.UpdateAnnotationList();
1715 private void EmissionMenu_ButtonPressEvent(
object o, ButtonPressEventArgs args)
1717 if (emissionMenu.Active)
1718 emissionMenu.Active =
false;
1720 emissionMenu.Active =
true;
1721 filteredMenu.Active =
false;
1722 rawMenu.Active =
false;
1723 rgbMenu.Active =
false;
1724 App.viewer.Mode = ImageView.ViewMode.Emission;
1725 BioLib.Recorder.Record(
"App.viewer.Mode = ImageView.ViewMode.RGBImage;");
1732 private void RawMenu_ButtonPressEvent(
object o, ButtonPressEventArgs args)
1735 rawMenu.Active =
false;
1737 rawMenu.Active =
true;
1738 filteredMenu.Active =
false;
1739 rgbMenu.Active =
false;
1740 emissionMenu.Active =
false;
1741 App.viewer.Mode = ImageView.ViewMode.Raw;
1742 BioLib.Recorder.Record(
"PlateTool.Create();");
1750 private void FilteredMenu_ButtonPressEvent(
object o, ButtonPressEventArgs args)
1752 if (filteredMenu.Active)
1753 filteredMenu.Active =
false;
1755 filteredMenu.Active =
true;
1756 rgbMenu.Active =
false;
1757 rawMenu.Active =
false;
1758 emissionMenu.Active =
false;
1759 App.viewer.Mode = ImageView.ViewMode.Filtered;
1760 BioLib.Recorder.Record(
"App.viewer.Mode = ImageView.ViewMode.Filtered;");
1767 private void RgbMenu_ButtonPressEvent(
object o, ButtonPressEventArgs args)
1770 rgbMenu.Active =
false;
1772 rgbMenu.Active =
true;
1773 filteredMenu.Active =
false;
1774 rawMenu.Active =
false;
1775 emissionMenu.Active =
false;
1776 App.viewer.Mode = ImageView.ViewMode.RGBImage;
1777 BioLib.Recorder.Record(
"App.viewer.Mode = ImageView.ViewMode.RGBImage;");
1786 BioLib.Recorder.Record(
"tabsView.Page = " + i +
";");
1791 public void AddTab(BioImage im,
string? sourcePath =
null)
1798 for (
int vi = 0; vi < viewers.Count; vi++)
1800 for (
int i = 0; i < viewers[vi].Images.Count; i++)
1802 if (viewers[vi].Images[i].Filename == im.Filename)
1804 RememberRecentImage(sourcePath ?? im.SourceFile);
1809 TryLoadZarrROIs(im);
1811 v.RefreshZarrLabelOverlays(sourcePath ?? im.file);
1813 Label dummy =
new Gtk.Label(System.IO.Path.GetDirectoryName(im.file.Replace(
"\\",
"/")) +
"/" + im.Filename);
1814 dummy.Text = im.file.Replace(
"\\",
"/") +
"/" + im.Filename;
1815 dummy.Visible =
false;
1816 Label l =
new Gtk.Label(System.IO.Path.GetFileName(im.Filename));
1817 l.Text = System.IO.Path.GetFileName(im.Filename);
1818 tabsView.AppendPage(dummy, l);
1819 Console.WriteLine(
"New tab added for " + im.Filename);
1820 App.nodeView.UpdateItems();
1823 RememberRecentImage(sourcePath ?? im.SourceFile);
1824 BioLib.Recorder.Record(
"AddTab(" + (im?.file ??
string.Empty) +
", " + Q(sourcePath ??
string.Empty) +
");");
1825 GLib.Timeout.Add(16, () =>
1827 if (v.View ==
null || !v.View.IsRealized || v.View.AllocatedWidth <= 1 || v.View.AllocatedHeight <= 1)
1842 BioLib.Recorder.Record(
"AddTab(" + (im?.file ??
string.Empty) +
", " + Q(sourcePath ??
string.Empty) +
");");
1844 Gtk.Application.Invoke((sender, args) =>
1848 for (
int vi = 0; vi < viewers.Count; vi++)
1850 for (
int i = 0; i < viewers[vi].Images.Count; i++)
1852 if (viewers[vi].Images[i].Filename == im.Filename)
1854 RememberRecentImage(sourcePath ?? im.SourceFile);
1861 v.RefreshZarrLabelOverlays(sourcePath ?? im.file);
1862 Label dummy =
new Gtk.Label(System.IO.Path.GetDirectoryName(im.file.Replace(
"\\",
"/")) +
"/" + im.Filename);
1863 dummy.Text = im.file.Replace(
"\\",
"/") +
"/" + im.Filename;
1864 dummy.Visible =
false;
1865 Label l =
new Gtk.Label(System.IO.Path.GetFileName(im.Filename));
1866 l.Text = System.IO.Path.GetFileName(im.Filename);
1867 tabsView.AppendPage(dummy, l);
1868 Console.WriteLine(
"New tab added for " + im.Filename);
1869 App.nodeView.UpdateItems();
1872 RememberRecentImage(sourcePath ?? im.SourceFile);
1873 GLib.Timeout.Add(16, () =>
1875 if (v.View ==
null || !v.View.IsRealized || v.View.AllocatedWidth <= 1 || v.View.AllocatedHeight <= 1)
1891 private static void TryLoadZarrROIs(BioImage image)
1895 string zarrDir = image.file;
1896 if (
string.IsNullOrWhiteSpace(zarrDir))
1899 if (!Directory.Exists(zarrDir) && !zarrDir.EndsWith(
".zarr", StringComparison.OrdinalIgnoreCase))
1905 image.Annotations.AddRange(rois.Where(r =>
1907 (
string.IsNullOrWhiteSpace(r.roiID) ||
1908 !r.roiID.StartsWith(
"zarr-label:", StringComparison.OrdinalIgnoreCase))));
1911 catch (Exception ex)
1913 Console.WriteLine($
"Load Zarr ROIs failed: {ex.Message}");
1917 private static void TrySaveZarrROIs(BioImage image,
string? overrideDir =
null)
1924 string zarrDir = overrideDir ?? image.file;
1925 if (
string.IsNullOrWhiteSpace(zarrDir))
1928 if (!Directory.Exists(zarrDir) && !zarrDir.EndsWith(
".zarr", StringComparison.OrdinalIgnoreCase))
1933 catch (Exception ex)
1935 Console.WriteLine($
"Save Zarr ROIs failed: {ex.Message}");
1939 private static void MergeZarrLabelOverlaysIntoAnnotations(BioImage image)
1941 if (image ==
null || App.viewer ==
null)
1944 var overlays = App.viewer.GetZarrLabelOverlaysForImage(image);
1945 if (overlays ==
null || overlays.Count == 0)
1948 foreach (var overlay
in overlays)
1950 if (overlay ==
null)
1953 if (
string.IsNullOrWhiteSpace(overlay.roiID) ||
1954 !overlay.roiID.StartsWith(
"zarr-label:", StringComparison.OrdinalIgnoreCase))
1957 bool exists = image.Annotations.Any(a => a !=
null && a.roiID == overlay.roiID);
1961 image.Annotations.Add(overlay);
1965 private static void TrySaveZarrLabelOverlays(BioImage image,
string? overrideDir =
null)
1972 string zarrDir = overrideDir ?? image.file;
1973 if (
string.IsNullOrWhiteSpace(zarrDir))
1976 if (!Directory.Exists(zarrDir) && !zarrDir.EndsWith(
".zarr", StringComparison.OrdinalIgnoreCase))
1979 if (image.Annotations ==
null || image.Annotations.Count == 0)
1982 BioLib.
Zarr.SaveLabelOverlays(image, image.Annotations, zarrDir);
1984 catch (Exception ex)
1986 Console.WriteLine($
"Save Zarr label overlays failed: {ex.Message}");
1990 Gtk.FileChooserDialog filechooser;
2000 filechooser =
new FileChooserDialog(
"Choose file to open",
this, FileChooserAction.Open,
"Cancel", ResponseType.Cancel,
"OK", ResponseType.Accept);
2001 filechooser.SelectMultiple =
true;
2002 if (filechooser.Run() != (
int)ResponseType.Accept)
2004 string[] sts = filechooser.Filenames;
2006 foreach (
string item
in sts)
2008 StartProgress(
"Open File",
"Opening");
2011 await FlushGtkEventsAsync();
2012 await OpenPyramidRootsAsTabsAsync(item);
2019 BioLib.Recorder.Record(
"BioImage.OpenAsync(" + StrArray(sts) +
", false, true, true);");
2030 filechooser =
new FileChooserDialog(
"Choose OME file to open",
this, FileChooserAction.Open,
"Cancel", ResponseType.Cancel,
"OK", ResponseType.Accept);
2031 filechooser.SelectMultiple =
true;
2032 if (filechooser.Run() != (
int)ResponseType.Accept)
2035 string[] sts = filechooser.Filenames;
2036 foreach (
string item
in filechooser.Filenames)
2038 StartProgress(
"Open OME",
"Opening");
2039 await OpenPyramidRootsAsTabsAsync(item);
2042 BioLib.Recorder.Record(
"BioImage.OpenAsync(" + StrArray(sts) +
", true, true, true);");
2053 filechooser =
new FileChooserDialog(
"Choose OME Series file to open.",
this, FileChooserAction.Open,
"Cancel", ResponseType.Cancel,
"OK", ResponseType.Accept);
2054 filechooser.SelectMultiple =
true;
2055 if (filechooser.Run() != (
int)ResponseType.Accept)
2058 string[] sts = filechooser.Filenames;
2059 foreach (
string item
in filechooser.Filenames)
2061 StartProgress(
"Open OME",
"Opening");
2062 await OpenPyramidRootsAsTabsAsync(item);
2065 BioLib.Recorder.Record(
"BioImage.OpenAsync(" + StrArray(sts) +
", true, true, true);");
2077 filechooser =
new FileChooserDialog(
"Choose series file to open",
this, FileChooserAction.Open,
"Cancel", ResponseType.Cancel,
"OK", ResponseType.Accept);
2078 filechooser.SelectMultiple =
true;
2079 if (filechooser.Run() != (
int)ResponseType.Accept)
2082 string[] sts = filechooser.Filenames;
2083 foreach (
string item
in filechooser.Filenames)
2085 StartProgress(
"Open OME Series",
"Opening");
2086 await OpenPyramidRootsAsTabsAsync(item);
2090 BioLib.Recorder.Record(
"BioImage.ImportROIsCSV(" + StrArray(sts) +
");");
2092 private async
void openQuPathProjectMenuClick(
object sender, EventArgs a)
2094 filechooser =
new FileChooserDialog(
"Choose QuPath project file to open",
this, FileChooserAction.Open,
"Cancel", ResponseType.Cancel,
"OK", ResponseType.Accept);
2095 filechooser.SelectMultiple =
true;
2096 if (filechooser.Run() != (
int)ResponseType.Accept)
2099 QuPath.Project pr = QuPath.OpenProject(filechooser.Filename);
2100 foreach (var item
in pr.Images)
2102 string file = item.ServerBuilder.Uri.Replace(
"file:/",
"");
2103 BioImage bm = await BioImage.OpenFile(file);
2105 string[] rs = Directory.GetFiles(System.IO.Path.GetDirectoryName(file));
2106 foreach (
string s
in rs)
2108 if (s.EndsWith(
".geojson") && s.Contains(System.IO.Path.GetFileNameWithoutExtension(file)))
2110 ROI[] rois = QuPath.ReadROI(s, bm);
2111 bm.Annotations.AddRange(rois);
2115 BioLib.Recorder.Record(
"QuPath.OpenProject(" + Q(filechooser.Filename) +
");");
2117 private void saveQuPathProject_ButtonPressEvent(
object o, ButtonPressEventArgs args)
2119 Gtk.FileChooserDialog filechooser =
2120 new Gtk.FileChooserDialog(
"Set QuPath project file to save.",
2122 FileChooserAction.Save,
2123 "Cancel", ResponseType.Cancel,
2124 "Save", ResponseType.Accept);
2125 if (filechooser.Run() != (
int)ResponseType.Accept)
2127 List<BioImage[]> bms =
new List<BioImage[]>();
2128 foreach (var item
in viewers)
2130 List<BioImage> bs =
new List<BioImage>();
2131 foreach (var b
in item.Images)
2135 bms.Add(bs.ToArray());
2137 QuPath.Project.SaveProject(filechooser.Filename, bms);
2138 BioLib.Recorder.Record(
"QuPath.Project.SaveProject(" + Q(filechooser.Filename) +
", bms);");
2149 filechooser =
new FileChooserDialog(
"Choose file to add to current tab.",
this, FileChooserAction.Open,
"Cancel", ResponseType.Cancel,
"OK", ResponseType.Accept);
2150 filechooser.SelectMultiple =
true;
2151 if (filechooser.Run() != (
int)ResponseType.Accept)
2154 string[] sts = filechooser.Filenames;
2155 foreach (
string item
in filechooser.Filenames)
2157 StartProgress(
"Add Image to Tab",
"Opening");
2158 await BioImage.OpenAsync(item,
false,
false,
true, 0);
2159 BioImage im = Images.GetImage(item);
2164 BioLib.Recorder.Record(
"BioImage.OpenAsync(" + StrArray(sts) +
", false, false, true);");
2175 filechooser =
new FileChooserDialog(
"Choose OME file to add to current tab.",
this, FileChooserAction.Open,
"Cancel", ResponseType.Cancel,
"OK", ResponseType.Accept);
2176 filechooser.SelectMultiple =
true;
2177 if (filechooser.Run() != (
int)ResponseType.Accept)
2180 string[] sts = filechooser.Filenames;
2181 foreach (
string item
in filechooser.Filenames)
2183 StartProgress(
"Add Image to Tab",
"Opening");
2184 await BioImage.OpenAsync(item,
true,
false,
true, 0);
2185 BioImage im = Images.GetImage(item);
2190 BioLib.Recorder.Record(
"BioImage.OpenAsync(" + StrArray(sts) +
", true, false, true);");
2192 private static bool done =
false;
2193 private static bool progressShown =
false;
2194 private static int progressShowThreshold = 0;
2195 private static int progressDisplayOffset = 0;
2196 private static int progressRunId = 0;
2197 private static readonly Stopwatch progressStopwatch =
new Stopwatch();
2199 private static string title;
2200 private static string FormatProgressTiming(
double progressPercent)
2202 TimeSpan elapsed = progressStopwatch.Elapsed;
2203 string elapsedText = FormatDuration(elapsed);
2204 string etaText =
"--:--";
2206 if (progressPercent > 0.01 && progressPercent < 100)
2208 double remainingSeconds = elapsed.TotalSeconds * ((100.0 - progressPercent) / progressPercent);
2209 if (
double.IsFinite(remainingSeconds) && remainingSeconds >= 0)
2210 etaText = FormatDuration(TimeSpan.FromSeconds(remainingSeconds));
2212 else if (progressPercent >= 100)
2217 return $
"Elapsed: {elapsedText} ETA: {etaText}";
2219 private static string FormatDuration(TimeSpan span)
2221 if (span.TotalHours >= 1)
2222 return span.ToString(
@"hh\:mm\:ss");
2223 return span.ToString(
@"mm\:ss");
2225 private static void ProgressUpdate(
int runId)
2229 float displayProgress = BioImage.Progress;
2230 if (progressDisplayOffset > 0)
2232 float normalized = (BioImage.Progress - progressDisplayOffset) * 100f / Math.Max(1, 100 - progressDisplayOffset);
2233 displayProgress = Math.Max(0, Math.Min(100, normalized));
2235 progress.Title = title;
2236 if (!
string.IsNullOrEmpty(BioImage.Status))
2237 progress.Status = BioImage.Status;
2238 if (!progressShown && BioImage.Progress >= progressShowThreshold)
2240 progressShown =
true;
2241 Gtk.Application.Invoke(delegate
2243 if (done || runId != progressRunId)
2248 progress.ProgressPercentValue = displayProgress;
2249 progress.Text = FormatProgressTiming(displayProgress);
2251 }
while (!done && runId == progressRunId);
2253 private static void StartProgress(
string titl,
string status)
2255 StartProgressInternal(titl, status,
true, 0, 0);
2257 private static void StartProgressHidden(
string titl,
string status)
2259 StartProgressInternal(titl, status,
false, 85, 85);
2261 private static void StartProgressInternal(
string titl,
string status,
bool showImmediately,
int showThreshold,
int displayOffset)
2265 int runId = progressRunId;
2266 progressShown = showImmediately;
2267 progressShowThreshold = showThreshold;
2268 progressDisplayOffset = displayOffset;
2270 BioImage.Progress = 0;
2271 progress.Status = status;
2272 progress.Title = title;
2273 if (BioImage.Status ==
"" || BioImage.Status ==
null)
2274 progress.Status = status;
2276 progress.Status = BioImage.Status;
2277 progress.ProgressPercentValue = BioImage.Progress;
2278 progressStopwatch.Restart();
2279 progress.Text = FormatProgressTiming(BioImage.Progress);
2280 if (showImmediately)
2284 Thread th =
new Thread(() => ProgressUpdate(runId));
2285 th.IsBackground =
true;
2289 private static async Task FlushGtkEventsAsync()
2292 while (Gtk.Application.EventsPending())
2293 Gtk.Application.RunIteration(
false);
2294 await Task.Delay(50);
2295 while (Gtk.Application.EventsPending())
2296 Gtk.Application.RunIteration(
false);
2299 private static void StopProgress()
2302 progressStopwatch.Stop();
2303 int runId = ++progressRunId;
2304 Gtk.Application.Invoke(delegate
2306 if (runId != progressRunId)
2312 private static bool IsZarrSource(BioImage image)
2317 string source = image.file;
2318 if (
string.IsNullOrWhiteSpace(source))
2319 source = image.Filename;
2321 return source.Contains(
".zarr", StringComparison.OrdinalIgnoreCase) ||
2322 source.StartsWith(
"https://", StringComparison.OrdinalIgnoreCase) ||
2323 source.StartsWith(
"s3://", StringComparison.OrdinalIgnoreCase);
2326 private static async Task<(BioImage Image,
bool IsTemporary)> GetOmeExportImageAsync(BioImage image)
2329 return (
null,
false);
2331 if (!IsZarrSource(image))
2332 return (image,
false);
2334 string source = image.file;
2335 if (
string.IsNullOrWhiteSpace(source))
2336 source = image.Filename;
2338 BioImage exportImage = await BioImage.OpenZarrAsync(source, 0, 0, image.ID).ConfigureAwait(
false);
2339 return (exportImage,
true);
2342 private static void ReleaseTemporaryExportImage(BioImage image)
2347 Images.images.Remove(image);
2360 filechooser =
new FileChooserDialog(
"Save Selected Image To TIFF",
this, FileChooserAction.Save,
"Cancel", ResponseType.Cancel,
"OK", ResponseType.Accept);
2361 if (filechooser.Run() != (
int)ResponseType.Accept)
2364 StartProgress(
"Save Selected Tiff",
"Saving");
2365 await BioImage.SaveAsync(filechooser.Filename,
ImageView.SelectedImage.Filename, 0,
false);
2376 filechooser =
new FileChooserDialog(
"Save Selected OME Image to File",
this, FileChooserAction.Save,
"Cancel", ResponseType.Cancel,
"OK", ResponseType.Accept);
2377 if (filechooser.Run() != (
int)ResponseType.Accept)
2380 StartProgress(
"Save Selected OME",
"Saving");
2381 var (exportImage, isTemporary) = await GetOmeExportImageAsync(
ImageView.SelectedImage);
2384 PreserveSourceAnnotations(
ImageView.SelectedImage, exportImage);
2385 NormalizeMasksForOmeExport(exportImage);
2386 await Task.Run(() => BioImage.SaveOMESeries(
new[] { exportImage }, filechooser.Filename, BioImage.Planes));
2391 ReleaseTemporaryExportImage(exportImage);
2403 filechooser =
new FileChooserDialog(
"Save Tab as an OME image.",
this, FileChooserAction.Save,
"Cancel", ResponseType.Cancel,
"OK", ResponseType.Accept);
2404 if (filechooser.Run() != (
int)ResponseType.Accept)
2407 StartProgress(
"Save Selected OME",
"Saving");
2408 BioImage[] exportImages =
new BioImage[App.viewer.Images.Count];
2409 List<BioImage> temporaryImages =
new List<BioImage>();
2412 for (
int i = 0; i < App.viewer.Images.Count; i++)
2414 BioImage sourceImage = App.viewer.Images[i];
2415 var (exportImage, isTemporary) = await GetOmeExportImageAsync(sourceImage);
2416 PreserveSourceAnnotations(sourceImage, exportImage);
2417 NormalizeMasksForOmeExport(exportImage);
2418 exportImages[i] = exportImage;
2420 temporaryImages.Add(exportImage);
2423 await Task.Run(() => BioImage.SaveOMESeries(exportImages, filechooser.Filename,
true));
2427 foreach (BioImage temp
in temporaryImages)
2428 ReleaseTemporaryExportImage(temp);
2440 filechooser =
new FileChooserDialog(
"Save Tab as an image.",
this, FileChooserAction.Save,
"Cancel", ResponseType.Cancel,
"OK", ResponseType.Accept);
2441 if (filechooser.Run() != (
int)ResponseType.Accept)
2444 StartProgress(
"Save Selected Tiff",
"Saving");
2445 await BioImage.SaveSeriesAsync(App.viewer.Images.ToArray(), filechooser.Filename,
false);
2454 filechooser =
new FileChooserDialog(
"Save TIFF series.",
this, FileChooserAction.Save,
"Cancel", ResponseType.Cancel,
"OK", ResponseType.Accept);
2455 if (filechooser.Run() != (
int)ResponseType.Accept)
2458 StartProgress(
"Save Selected Tiff",
"Saving");
2459 await BioImage.SaveSeriesAsync(App.viewer.Images.ToArray(), filechooser.Filename,
true);
2468 Gtk.FileChooserDialog filechooser =
2469 new Gtk.FileChooserDialog(
"Choose images to convert to stack.",
2471 FileChooserAction.Save,
2472 "Cancel", ResponseType.Cancel,
2473 "Save", ResponseType.Accept);
2474 filechooser.SelectMultiple =
true;
2475 if (filechooser.Run() != (
int)ResponseType.Accept)
2478 string[] sts = filechooser.Filenames;
2479 BioImage b = BioImage.ImagesToStack(sts,
true);
2489 if(App.tools ==
null)
2492 App.tools.Present();
2502 App.setTool.Present();
2512 App.roiManager.Show();
2513 App.roiManager.Present();
2522 Gtk.FileChooserDialog filechooser =
new Gtk.FileChooserDialog(
"Set filename for export.",
2524 FileChooserAction.Save,
2525 "Cancel", ResponseType.Cancel,
2526 "Ok", ResponseType.Ok);
2527 filechooser.SelectMultiple =
false;
2528 if (filechooser.Run() != (
int)ResponseType.Ok)
2530 BioImage.ExportROIsCSV(filechooser.Filename,
ImageView.SelectedImage.Annotations);
2532 BioLib.Recorder.Record(
"BioImage.ExportROIsCSV(" + Q(filechooser.Filename) +
", ImageView.SelectedImage.Annotations);");
2540 Gtk.FileChooserDialog filechooser =
new Gtk.FileChooserDialog(
"Choose the file to open.",
2542 FileChooserAction.Open,
2543 "Cancel", ResponseType.Cancel,
2544 "Ok", ResponseType.Ok);
2545 filechooser.SelectMultiple =
true;
2546 if (filechooser.Run() != (
int)ResponseType.Ok)
2548 string[] sts = filechooser.Filenames;
2549 foreach (
string item
in filechooser.Filenames)
2551 ImageView.SelectedImage.Annotations.AddRange(BioImage.ImportROIsCSV(item));
2554 BioLib.Recorder.Record(
"BioImage.ImportROIsCSV(" + StrArray(sts) +
");");
2563 Gtk.FileChooserDialog folderchooser =
new Gtk.FileChooserDialog(
"Set folder of images for export.",
2565 FileChooserAction.SelectFolder,
2566 "Cancel", ResponseType.Cancel,
2567 "Ok", ResponseType.Ok);
2568 folderchooser.SelectMultiple =
true;
2569 if (folderchooser.Run() != (
int)ResponseType.Ok)
2571 string folder = folderchooser.Filename;
2572 folderchooser.Destroy();
2573 Gtk.FileChooserDialog filechooser =
new Gtk.FileChooserDialog(
"Set filename for CSV to export.",
2575 FileChooserAction.Save,
2576 "Cancel", ResponseType.Cancel,
2577 "Ok", ResponseType.Ok);
2578 filechooser.SelectMultiple =
false;
2579 if (filechooser.Run() != (
int)ResponseType.Ok)
2581 string file = filechooser.Filename;
2583 BioImage.ExportROIFolder(folder, file);
2584 BioLib.Recorder.Record(
"BioImage.ExportROIFolder(" + Q(folder) +
", " + Q(file) +
");");
2593 BioImage.AutoThreshold(
ImageView.SelectedImage,
true);
2594 if (
ImageView.SelectedImage.bitsPerPixel > 8)
2595 ImageView.SelectedImage.StackThreshold(
true);
2597 ImageView.SelectedImage.StackThreshold(
false);
2598 App.viewer.RefreshPyramidalTiles();
2599 App.viewer.UpdateImage();
2600 App.viewer.UpdateView();
2608 if (App.viewer ==
null)
2611 App.channelsTool.Show();
2612 App.channelsTool.Present();
2620 foreach (AForge.Bitmap bf in
ImageView.SelectedImage.Buffers)
2624 App.viewer.UpdateImage();
2625 App.viewer.UpdateView();
2636 App.stack.Present();
2688 App.filters.Present();
2699 App.funcs.Present();
2709 App.console.Present();
2718 App.scripting.Show();
2719 App.scripting.Present();
2730 App.about.Present();
2738 string shortName = System.IO.Path.GetFileName(tabName);
2739 for (
int i = tabsView.NPages - 1; i >= 0; i--)
2741 Widget page = tabsView.GetNthPage(i);
2743 Widget tabLabelWidget = tabsView.GetTabLabel(page);
2744 string tabText =
null;
2745 if (tabLabelWidget is Gtk.Label tl)
2750 string pageText =
null;
2751 if (page is Gtk.Label pl)
2754 bool match = (tabText !=
null && (tabText == tabName || tabText == shortName))
2755 || (pageText !=
null && (pageText == tabName
2756 || System.IO.Path.GetFileName(pageText) == shortName));
2760 tabsView.RemovePage(i);
2761 App.nodeView.UpdateItems();
2769 public async
void Open(
string file)
2771 await OpenPyramidRootsAsViewsAsync(file,
true);