-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Added HDF5 file support Through Plugins #1884
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
aaravmaloo
wants to merge
8
commits into
QL-Win:master
Choose a base branch
from
aaravmaloo:master
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
8 commits
Select commit
Hold shift + click to select a range
989c6a0
crate logic for hdf5 plugin
aaravmaloo fd909ec
update sln file
aaravmaloo d5322d6
update slnx to include hdf5 viewer
aaravmaloo f73dff1
update csproj
aaravmaloo 8dcb280
create hdf5 text panel
aaravmaloo a76470a
create summery builder for hdf5 plugin viewer
aaravmaloo ae4d4fc
Update Hdf5SummaryBuilder.cs
aaravmaloo 14c4b6f
Update Plugin.cs
aaravmaloo File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
153 changes: 153 additions & 0 deletions
153
QuickLook.Plugin/QuickLook.Plugin.Hdf5Viewer/Hdf5SummaryBuilder.cs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,153 @@ | ||
| using HDF5.NET; | ||
| using System; | ||
| using System.Collections.Generic; | ||
| using System.Linq; | ||
| using System.Text; | ||
|
|
||
| namespace QuickLook.Plugin.Hdf5Viewer; | ||
|
|
||
| internal static class Hdf5SummaryBuilder | ||
| { | ||
| private const int MaxDepth = 16; | ||
| private const int MaxChildrenPerGroup = 250; | ||
| private const int MaxAttributesPerObject = 20; | ||
|
|
||
| public static string Build(string path) | ||
| { | ||
| var sb = new StringBuilder(32 * 1024); | ||
|
|
||
| using (var file = H5File.OpenRead(path)) | ||
| { | ||
| sb.AppendLine("HDF5 structure summary"); | ||
| sb.AppendLine(); | ||
|
|
||
| AppendObject(file, sb, depth: 0); | ||
| } | ||
|
|
||
| return sb.ToString(); | ||
| } | ||
|
|
||
| private static void AppendObject(H5Object h5Object, StringBuilder sb, int depth) | ||
| { | ||
| if (depth > MaxDepth) | ||
| { | ||
| sb.AppendLine($"{Indent(depth)}... depth limit reached"); | ||
| return; | ||
| } | ||
|
|
||
| switch (h5Object) | ||
| { | ||
| case H5Group group: | ||
| AppendGroup(group, sb, depth); | ||
| return; | ||
| case H5Dataset dataset: | ||
| AppendDataset(dataset, sb, depth); | ||
| return; | ||
| case H5CommitedDatatype committedDatatype: | ||
| sb.AppendLine($"{Indent(depth)}[DATATYPE] {SafeName(committedDatatype.Name)}"); | ||
| return; | ||
| case H5UnresolvedLink unresolvedLink: | ||
| sb.AppendLine($"{Indent(depth)}[UNRESOLVED] {SafeName(unresolvedLink.Name)}"); | ||
| return; | ||
| default: | ||
| sb.AppendLine($"{Indent(depth)}[{h5Object.GetType().Name}] {SafeName(h5Object.Name)}"); | ||
| return; | ||
| } | ||
| } | ||
|
|
||
| private static void AppendGroup(H5Group group, StringBuilder sb, int depth) | ||
| { | ||
| sb.AppendLine($"{Indent(depth)}[GROUP] {SafeName(group.Name)}"); | ||
| AppendAttributes(group, sb, depth + 1); | ||
|
|
||
| var children = SafeReadChildren(group).ToList(); | ||
| var visibleChildren = children.Take(MaxChildrenPerGroup).ToList(); | ||
|
|
||
| foreach (var child in visibleChildren) | ||
| AppendObject(child, sb, depth + 1); | ||
|
|
||
| if (children.Count > MaxChildrenPerGroup) | ||
| sb.AppendLine($"{Indent(depth + 1)}... {children.Count - MaxChildrenPerGroup} more children"); | ||
| } | ||
|
|
||
| private static void AppendDataset(H5Dataset dataset, StringBuilder sb, int depth) | ||
| { | ||
| var dimensions = string.Join(" x ", dataset.Space.Dimensions.Select(d => d.ToString())); | ||
| var shape = string.IsNullOrWhiteSpace(dimensions) ? "scalar" : dimensions; | ||
|
|
||
| sb.AppendLine( | ||
| $"{Indent(depth)}[DATASET] {SafeName(dataset.Name)} | " + | ||
| $"shape={shape}, dtype={dataset.Type.Class}, itemSize={dataset.Type.Size}B, layout={dataset.Layout.Class}"); | ||
|
|
||
| AppendAttributes(dataset, sb, depth + 1); | ||
| } | ||
|
|
||
| private static void AppendAttributes(H5AttributableObject attributable, StringBuilder sb, int depth) | ||
| { | ||
| var visible = new List<H5Attribute>(MaxAttributesPerObject); | ||
| var hasMoreAttributes = false; | ||
|
|
||
| try | ||
| { | ||
| var count = 0; | ||
|
|
||
| foreach (var attribute in attributable.Attributes) | ||
| { | ||
| count++; | ||
|
|
||
| if (count <= MaxAttributesPerObject) | ||
| { | ||
| visible.Add(attribute); | ||
| } | ||
| else | ||
| { | ||
| hasMoreAttributes = true; | ||
| break; | ||
| } | ||
| } | ||
| } | ||
| catch (Exception ex) | ||
| { | ||
| sb.AppendLine($"{Indent(depth)}@attributes: <error: {ex.Message}>"); | ||
| return; | ||
| } | ||
|
|
||
| if (visible.Count == 0) | ||
| return; | ||
|
|
||
| foreach (var attribute in visible) | ||
| { | ||
| var dimensions = string.Join(" x ", attribute.Space.Dimensions.Select(d => d.ToString())); | ||
| var shape = string.IsNullOrWhiteSpace(dimensions) ? "scalar" : dimensions; | ||
|
|
||
| sb.AppendLine( | ||
| $"{Indent(depth)}@{SafeName(attribute.Name)} " + | ||
| $"(type={attribute.Type.Class}, shape={shape}, itemSize={attribute.Type.Size}B)"); | ||
| } | ||
|
|
||
| if (hasMoreAttributes) | ||
| sb.AppendLine($"{Indent(depth)}... more attributes"); | ||
| } | ||
|
|
||
| private static IEnumerable<H5Object> SafeReadChildren(H5Group group) | ||
| { | ||
| try | ||
| { | ||
| return group.Children; | ||
| } | ||
| catch | ||
| { | ||
| return Array.Empty<H5Object>(); | ||
| } | ||
| } | ||
|
|
||
| private static string SafeName(string name) | ||
| { | ||
| return string.IsNullOrEmpty(name) ? "/" : name; | ||
| } | ||
|
|
||
| private static string Indent(int level) | ||
| { | ||
| return new string(' ', level * 2); | ||
| } | ||
| } | ||
32 changes: 32 additions & 0 deletions
32
QuickLook.Plugin/QuickLook.Plugin.Hdf5Viewer/Hdf5TextPanel.cs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,32 @@ | ||
| using System.Windows.Controls; | ||
| using System.Windows.Media; | ||
|
|
||
| namespace QuickLook.Plugin.Hdf5Viewer; | ||
|
|
||
| public sealed class Hdf5TextPanel : UserControl | ||
| { | ||
| private readonly TextBox _textBox; | ||
|
|
||
| public Hdf5TextPanel() | ||
| { | ||
| _textBox = new TextBox | ||
| { | ||
| IsReadOnly = true, | ||
| TextWrapping = System.Windows.TextWrapping.NoWrap, | ||
| VerticalScrollBarVisibility = ScrollBarVisibility.Auto, | ||
| HorizontalScrollBarVisibility = ScrollBarVisibility.Auto, | ||
| FontFamily = new FontFamily("Consolas"), | ||
| FontSize = 13, | ||
| BorderThickness = new System.Windows.Thickness(0), | ||
| Padding = new System.Windows.Thickness(12, 8, 12, 8) | ||
| }; | ||
|
|
||
| Content = _textBox; | ||
| } | ||
|
|
||
| public void SetText(string text) | ||
| { | ||
| _textBox.Text = text ?? string.Empty; | ||
| _textBox.CaretIndex = 0; | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,109 @@ | ||
| using QuickLook.Common.Plugin; | ||
| using System; | ||
| using System.IO; | ||
| using System.Linq; | ||
| using System.Threading.Tasks; | ||
| using System.Windows; | ||
|
|
||
| namespace QuickLook.Plugin.Hdf5Viewer; | ||
|
|
||
| public sealed class Plugin : IViewer | ||
| { | ||
| private static readonly byte[] Hdf5Signature = { 0x89, 0x48, 0x44, 0x46, 0x0D, 0x0A, 0x1A, 0x0A }; | ||
| private static readonly string[] SupportedExtensions = { ".h5", ".hdf5", ".hdf", ".he5" }; | ||
| private static readonly long[] SignatureProbeOffsets = { 0, 512, 1024, 2048, 4096, 8192, 16384, 32768, 65536, 131072, 262144, 524288 }; | ||
| private Hdf5TextPanel _panel; | ||
|
|
||
| public int Priority => 0; | ||
|
|
||
| public void Init() | ||
| { | ||
| } | ||
|
|
||
| public bool CanHandle(string path) | ||
| { | ||
| if (Directory.Exists(path)) | ||
| return false; | ||
|
|
||
| var extension = Path.GetExtension(path); | ||
| if (!SupportedExtensions.Contains(extension, StringComparer.OrdinalIgnoreCase)) | ||
| return false; | ||
|
|
||
| return HasHdf5Signature(path); | ||
| } | ||
|
|
||
| public void Prepare(string path, ContextObject context) | ||
| { | ||
| context.PreferredSize = new Size { Width = 1100, Height = 760 }; | ||
| } | ||
|
|
||
| public void View(string path, ContextObject context) | ||
| { | ||
| _panel = new Hdf5TextPanel(); | ||
| context.ViewerContent = _panel; | ||
| context.Title = Path.GetFileName(path); | ||
| context.IsBusy = true; | ||
|
|
||
| var panel = _panel; | ||
|
|
||
| Task.Run(() => | ||
| { | ||
| try | ||
| { | ||
| return Hdf5SummaryBuilder.Build(path); | ||
| } | ||
| catch (Exception ex) | ||
| { | ||
| return | ||
| $"Failed to open HDF5 file.{Environment.NewLine}{Environment.NewLine}" + | ||
| $"{ex.GetType().Name}: {ex.Message}"; | ||
| } | ||
| }).ContinueWith(t => | ||
| { | ||
| if (panel is not null) | ||
| panel.SetText(t.Result); | ||
|
|
||
| context.IsBusy = false; | ||
| }, TaskScheduler.FromCurrentSynchronizationContext()); | ||
| } | ||
|
|
||
| public void Cleanup() | ||
| { | ||
| _panel = null; | ||
| } | ||
|
|
||
| private static bool HasHdf5Signature(string path) | ||
| { | ||
| try | ||
| { | ||
| using (var stream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) | ||
| { | ||
| if (stream.Length < Hdf5Signature.Length) | ||
| return false; | ||
|
|
||
| var header = new byte[Hdf5Signature.Length]; | ||
|
|
||
| foreach (var offset in SignatureProbeOffsets) | ||
| { | ||
| if (offset + Hdf5Signature.Length > stream.Length) | ||
| break; | ||
|
|
||
| stream.Position = offset; | ||
|
|
||
| var read = stream.Read(header, 0, header.Length); | ||
| if (read != header.Length) | ||
| continue; | ||
|
|
||
| if (header.SequenceEqual(Hdf5Signature)) | ||
| return true; | ||
| } | ||
| } | ||
| } | ||
| catch | ||
| { | ||
| return false; | ||
| } | ||
|
|
||
| return false; | ||
| } | ||
| } |
75 changes: 75 additions & 0 deletions
75
QuickLook.Plugin/QuickLook.Plugin.Hdf5Viewer/QuickLook.Plugin.Hdf5Viewer.csproj
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,75 @@ | ||
| <Project Sdk="Microsoft.NET.Sdk.WindowsDesktop"> | ||
|
|
||
| <PropertyGroup> | ||
| <OutputType>Library</OutputType> | ||
| <TargetFramework>net462</TargetFramework> | ||
| <RootNamespace>QuickLook.Plugin.Hdf5Viewer</RootNamespace> | ||
| <AssemblyName>QuickLook.Plugin.Hdf5Viewer</AssemblyName> | ||
| <FileAlignment>512</FileAlignment> | ||
| <SignAssembly>false</SignAssembly> | ||
| <UseWPF>true</UseWPF> | ||
| <LangVersion>latest</LangVersion> | ||
| <GenerateAssemblyInfo>false</GenerateAssemblyInfo> | ||
| <AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath> | ||
| <GenerateTargetFrameworkAttribute>false</GenerateTargetFrameworkAttribute> | ||
| <CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet> | ||
| <ProjectGuid>{69D60E22-9190-4433-9A6E-1D889CF5CA52}</ProjectGuid> | ||
| </PropertyGroup> | ||
|
|
||
| <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|AnyCPU'"> | ||
| <DebugSymbols>true</DebugSymbols> | ||
| <DebugType>full</DebugType> | ||
| <Optimize>false</Optimize> | ||
| <OutputPath>..\..\Build\Debug\QuickLook.Plugin\QuickLook.Plugin.Hdf5Viewer\</OutputPath> | ||
| <DefineConstants>DEBUG;TRACE</DefineConstants> | ||
| <PlatformTarget>AnyCPU</PlatformTarget> | ||
| <ErrorReport>prompt</ErrorReport> | ||
| </PropertyGroup> | ||
|
|
||
| <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|AnyCPU'"> | ||
| <DebugType>pdbonly</DebugType> | ||
| <Optimize>true</Optimize> | ||
| <OutputPath>..\..\Build\Release\QuickLook.Plugin\QuickLook.Plugin.Hdf5Viewer\</OutputPath> | ||
| <DefineConstants>TRACE</DefineConstants> | ||
| <PlatformTarget>AnyCPU</PlatformTarget> | ||
| <ErrorReport>prompt</ErrorReport> | ||
| </PropertyGroup> | ||
|
|
||
| <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x86'"> | ||
| <DebugSymbols>true</DebugSymbols> | ||
| <DebugType>full</DebugType> | ||
| <Optimize>false</Optimize> | ||
| <OutputPath>..\..\Build\Debug\QuickLook.Plugin\QuickLook.Plugin.Hdf5Viewer\</OutputPath> | ||
| <DefineConstants>DEBUG;TRACE</DefineConstants> | ||
| <PlatformTarget>x86</PlatformTarget> | ||
| <ErrorReport>prompt</ErrorReport> | ||
| </PropertyGroup> | ||
|
|
||
| <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x86'"> | ||
| <DebugType>pdbonly</DebugType> | ||
| <Optimize>true</Optimize> | ||
| <OutputPath>..\..\Build\Release\QuickLook.Plugin\QuickLook.Plugin.Hdf5Viewer\</OutputPath> | ||
| <DefineConstants>TRACE</DefineConstants> | ||
| <PlatformTarget>x86</PlatformTarget> | ||
| <ErrorReport>prompt</ErrorReport> | ||
| </PropertyGroup> | ||
|
|
||
| <ItemGroup> | ||
| <PackageReference Include="HDF5.NET" Version="1.0.0-alpha.22" /> | ||
| </ItemGroup> | ||
|
|
||
| <ItemGroup> | ||
| <ProjectReference Include="..\..\QuickLook.Common\QuickLook.Common.csproj"> | ||
| <Project>{85FDD6BA-871D-46C8-BD64-F6BB0CB5EA95}</Project> | ||
| <Name>QuickLook.Common</Name> | ||
| <Private>False</Private> | ||
| </ProjectReference> | ||
| </ItemGroup> | ||
|
|
||
| <ItemGroup> | ||
| <Compile Include="..\..\GitVersion.cs"> | ||
| <Link>Properties\GitVersion.cs</Link> | ||
| </Compile> | ||
| </ItemGroup> | ||
|
|
||
| </Project> |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.