如何在Unity中等待Hololens 2的UWP文件选择器

huangapple go评论69阅读模式
英文:

How to await UWP Filepicker for Hololens 2 in Unity

问题

我正在为HoloLens 2使用Unity开发应用程序。我需要一些帮助理解如何在UWP环境和.NET/Unity环境之间进行通信和跳转。

我试图在按钮按下后使用FileOpenPicker的PickSingleFileAsync()函数来获取所选文件的路径,就像这个帖子中所述,我有类似的情景:

代码:

部分1:
一个方法调用StartPicker()来获取文件名,然后进行后续计算。但是这个方法等待结果 => await StartPicker()

public async Task<string> StartPicker()
{
    #if !UNITY_EDITOR && UNITY_WSA_10_0
    Debug.Log("HOLOLENS 2 PICKER");
    await FilePicker_Hololens();
    #endif

    #if UNITY_EDITOR
    Debug.Log("UNITY_STANDALONE PICKER");
    await FilePicker_Win();
    #endif

    //等待Picker返回路径
    //注意:目前返回空字符串,因为调用方法似乎已经完成任务
    return filePath;
}

部分2: 该方法应指示文件对话框是否已完成,因此已获取路径(LoadDataset() 负责实际加载,如果路径可用,则应在此等待)。

#if !UNITY_EDITOR && UNITY_WSA_10_0
private async Task FilePicker_Hololens()
{
    //必须在UI线程上调用UWP。
    UnityEngine.WSA.Application.InvokeOnUIThread(async () =>
    {
        var filepicker = new FileOpenPicker();
        filepicker.FileTypeFilter.Add("*");
        filepicker.SuggestedStartLocation = PickerLocationId.PicturesLibrary;

        var file = await filepicker.PickSingleFileAsync();

        //注意:在文件选择器关闭后调用
        UnityEngine.WSA.Application.InvokeOnAppThread(async () =>
        {
            filePath = (file != null) ? file.Path : "未选择任何文件";
            await LoadDataset();
        }, true);
    }, true);
    //在这里我们应该能够返回一个已完成的任务
}
#endif

问题:

我的问题是,似乎FilePicker_Hololens()即使文件对话框未完成也返回已完成的任务,因此未获取路径。我等待执行StartPicker()以及内部执行FilePicker_Hololens。尽管如此,调用StartPicker()的调用方同步执行其余方法,不会在await处离开方法。我认为问题出在UnityEngine.WSA.Application.InvokeOnUIThreadUnityEngine.WSA.Application.InvokeOnAppThread之间的UWP和Unity上下文之间的跳转。

如何等待对话框关闭?

英文:

I am developing an application for the HoloLens 2 with Unity. I need some help understanding how to communicate and jump between the UWP environment and the .NET/Unity environment.

I am trying to use the FileOpenPicker with the PickSingleFileAsync() function after a button press to get the path of a selected file. Like in this Post i have kind of this scenario:

Code:

Part1:
A method calls StartPicker() to get a file name with which subsequent calculations happen. But this method waits for the result => await StartPicker()

    public async Task&lt;String&gt; StartPicker()
    {
        #if !UNITY_EDITOR &amp;&amp; UNITY_WSA_10_0
        Debug.Log(&quot;HOLOLENS 2 PICKER&quot;);
        await FilePicker_Hololens();
        #endif

        #if UNITY_EDITOR
        Debug.Log(&quot;UNITY_STANDALONE PICKER&quot;);
        await FilePicker_Win();
        #endif

        //Wait for Picker retrieve path
        // Note: Currently returns empty string as calling method has Task seems to be finished
        return filePath;
    }

Part 2: Method which should indicate if FileDialog is finished and therefore path is obtained. (LoadDataset() takes care of the actual loading if the path is available and should be waited for here too).)

#if !UNITY_EDITOR &amp;&amp; UNITY_WSA_10_0
    private async Task FilePicker_Hololens()
    {
        // Calls to UWP must be made on the UI thread.
        UnityEngine.WSA.Application.InvokeOnUIThread(async () =&gt;
            {
                var filepicker = new FileOpenPicker();
                filepicker.FileTypeFilter.Add(&quot;*&quot;);
                filepicker.SuggestedStartLocation = PickerLocationId.PicturesLibrary;

                var file = await filepicker.PickSingleFileAsync();

                //Note: Called after the file picker is closed
                UnityEngine.WSA.Application.InvokeOnAppThread(async () =&gt;
                {
                    filePath = (file != null) ? file.Path : &quot;Nothing selected&quot;;
                    await LoadDataset();
                }, true);
            }, true);
     // Here we should be able to return a finished Task
    }
#endif

Problem:

My problem is now that it seems FilePicker_Hololens() returns a finished Task even if the FileDialog is not finished, hence no path is acquired. I await the execution StartPicker() as well as the execution of FilePicker_Hololens inside. Nevertheless, the caller of StartPicker() executes its remaining method synchronously and does not leave the method at await. I think my Problems lies in the jumping between UWP and Unity Context with UnityEngine.WSA.Application.InvokeOnUIThread and UnityEngine.WSA.Application.InvokeOnAppThread due to the delegates.

How can i await the closing of the dialog?

答案1

得分: 1

Your FilePicker_Hololens函数确实会立即返回,因为你只是将其余的操作安排在下一个UIThread运行中,但没有等待它实际发生。

你可以使用一个TaskCompletionSource 来实现,例如:

private async Task FilePicker_Hololens()
{
    var completionSource = new TaskCompletionSource<object>();

    UnityEngine.WSA.Application.InvokeOnUIThread(async () =>
    {
        var filepicker = new FileOpenPicker();
        filepicker.FileTypeFilter.Add("*");
        filepicker.SuggestedStartLocation = PickerLocationId.PicturesLibrary;

        var file = await filepicker.PickSingleFileAsync();

        UnityEngine.WSA.Application.InvokeOnAppThread(async () =>
        {
            filePath = (file != null) ? file.Path : "Nothing selected";

            // 应该在这里执行吗?
            await LoadDataset();

            // 设置完成源任务为已完成
            completionSource.SetResult(null);
        }, true);
    }, true);

    // 使此任务等待,直到完成源通过调用`SetResult`等完成
    await completionSource.Task;
}

通常,不必通过字段来访问filePath,你实际上可以直接将其作为Task<string>,并相应地使用TaskCompletionSource<string>,然后传入SetResult(filePath)。然后在StartPicker中,你可以实际上执行:

return await FilePicker_Hololens();
英文:

Your FilePicker_Hololens indeed returns immediately since you only schedule all the rest to happen in the next UIThread run but do not wait for this to actually have happened.

You could use a TaskCompletionSource and do e.g.

private async Task FilePicker_Hololens()
{
    var completionSource = new TaskCompletionSource();

    UnityEngine.WSA.Application.InvokeOnUIThread(async () =&gt;
    {
        var filepicker = new FileOpenPicker();
        filepicker.FileTypeFilter.Add(&quot;*&quot;);
        filepicker.SuggestedStartLocation = PickerLocationId.PicturesLibrary;

        var file = await filepicker.PickSingleFileAsync();

        UnityEngine.WSA.Application.InvokeOnAppThread(async () =&gt;
        {
            filePath = (file != null) ? file.Path : &quot;Nothing selected&quot;;

            // should this happen in here at all?
            await LoadDataset();

            // sets the completion source task to finished
            completionSource.SetResult();
        }, true);
    }, true);

    // keeps this task awaiting until the completion source finished by e.g. calling `SetResult`
    await completionSource.Task;
}

In general instead of going through fields for filePath you could actually rather directly make this also a Task&lt;string&gt; and use a TaskCompletionSource&lt;string&gt; accordingly and then pass in SetResult(filePath). Then in StartPicker you could actually do

return await FilePicker_Hololens();

huangapple
  • 本文由 发表于 2023年3月9日 18:10:54
  • 转载请务必保留本文链接:https://go.coder-hub.com/75683134.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定