教程 5:如何使用 Web 界面通过 Python 脚本远程控制 VRED

了解如何使用 Web 界面通过 Python 脚本远程控制 VRED。

下载示例脚本

跳转到脚本示例

下载教程 PDF

视频字幕:大家好,欢迎学习面向 VRED Pro 的下一个 Python 教程。我是主持人 Christopher,今天我将介绍如何使用 VRED 的 Web 界面通过 Python 脚本远程控制 VRED。

您可能已熟悉 VRED 的 Web 界面及其使用方法。激活后,VRED 将启动托管 Web 应用程序的 Web 服务器,以通过浏览器远程控制 VRED。它非常适合设计审阅或客户演示,因为您可以移动摄影机、拥有实时视频流,还可以触发您定义的任何变量集。

要启用 Web 界面,必须转到“首选项”并向下滚动到“Web 界面”。您可以在此处启用 Web 界面。当应用更改时,您基本上就可以在本地主机上使用 Web 界面了。但是,举例来说,如果您想使用 iPad 进行访问,则必须将主机访问设置为“无限制”,或者将您的 iPad 及其 IP 地址添加到主机列表中。

要进行测试,完全可以将主机访问设置为“无限制”,但为了安全起见,您应始终使用主机列表。当您位于同一网络中时,只需输入所使用设备的 IP 地址,即可连接到 Web 界面。还有一些其他设置,在本教程中我不会一一进行介绍,但所有设置都将在 Web 界面首选项联机文档中进行介绍。

现在,我已使用 iPad 连接到 VRED 场景,您会看到,我可以用手指控制场景,并在变量集之间切换。我可以拍摄快照、更改设置等等。因此,当您想要进行简单的演示时,这些功能可能已足够。但是,使用 VRED 流式处理应用程序还可以直接输入 Python 脚本,然后在终端中执行该脚本。此处已有一个示例,用于创建新场景并将长方体添加到场景图形中。

使用终端,可以从场景中执行任何 Python 代码。您可以直接使用 Python API 创建几何体(例如在终端示例中),或者调用可从场景中访问的函数(例如脚本编辑器中定义的函数或来自导入的 Python 模块的函数)。此处的终端与 VRED 中的脚本编辑器非常相似。将直接执行您输入的所有内容并与场景交互。

现在,您已了解如何使用 Python 终端通过默认流式传输应用程序控制 VRED。另一种调用自定义 Python 脚本的好方法是使用变量集。只需创建一个名为“Scripts”或您喜欢的其他任何名称的变量集组,并添加包含要执行的 Python 代码的变量集。这样,您就不必仅仅为了某个操作的按钮而对自定义界面进行编程。默认流式处理应用程序已为您处理此问题,并在变量集中创建了一个菜单项。

当然,也可以构建自己的 Web 界面以与 VRED 交互。执行此操作时,您可以完全自定义远程应用程序,并完全按照您的需要设置其样式。

首先,我向大家介绍如何使用端点与 VRED 交互。端点就是对包含要执行的 Python 代码的 Web 界面的 GET 请求。在这里,我将向大家举例说明如何选择变量集,以及如何在自定义 Web 应用程序中触发其他 Python 函数。我们可以将其他查询 Python 函数发送到 Web 界面提供的端点。

在第一个示例中,我们使用的是 Python 端点。它会向 VRED 发送 Python 命令,并且应该没有返回值。Python 命令与“value”参数一起提供,并且必须进行 URI 编码。单击该按钮后,我们将消息发送到端点,现在可以在终端中输出消息。我们使用相同的方法选择变量集。在这里,我使用场景中定义的变量集选择黑色金属材质。

还有另一个端点也可以从 Web 界面返回数据。此端点称为“pythoneval2”。与 Python 端点类似,我们可以向 VRED 发送命令,但现在还必须管理返回值。在本例中,我们请求所有视点,并接收我们输出到控制台的一组视点。您必须注意 GET 请求返回的数据格式。在本例中,它是一个可以求值为 JavaScript 数组的字符串。

然而,构建自定义 Web 界面的最新方法是使用 Autodesk 在 2021 版中引入的 Web API。Web API 是一个 JavaScript 应用程序,它集成在 Web 应用程序中,并允许完全访问 VRED 的 Python API,而无需使用 Python 端点。Web API 支持 Python API v2 中的所有模块和函数。

而要调用 API v1,您仍需像上一个示例中那样使用提供的端点。我们可以通过从 Web 界面导入脚本,将 Web API 添加到我们的应用程序中。我们创建了一个新的 JavaScript 模块,可以在其中导入 API。然后,添加一个函数以添加标注,并添加一个函数以收回所有标注。此外,每当将标注添加到 VRED 时,我们都会连接到信号事件。这是一个相当简单的设置,通常您会在一个完整的 Web 应用程序中包含此设置。在这里,我只是想向大家展示总体思路。

要使函数在 HTML 文档中可见,必须将其添加到全局范围。这可以通过向窗口对象添加函数来完成。在 HTML 文档中,我们只是以模块类型链接到新的 JavaScript 文件,现在可以在单击其中一个按钮时调用这些函数。

我们从这个示例中可以看到,通过引用服务并调用其函数,可以调用 JavaScript 函数,这与 Python 版本非常相似。我们还可以使用一些很棒的 JavaScript 功能,例如 Promise 和错误处理,以异步方式处理我们收到的数据。

要运行此示例,我必须使用本地 Web 服务器提供文件。这是因为本地运行时跨域资源策略不允许访问我们的 JavaScript 文件。为此,我使用随节点一起安装的工具 HTTP Server。当然,您也可以选择使用其他任何服务器。

我可以访问使用我的本地 Web 服务器提供的地址下的页面。当我单击按钮时,我们可以看到标注已添加到场景中,当我们请求所有标注的列表时,控制台也会显示新标注。Autodesk 还在 VRED 的 examples 目录中提供了其 Web 应用程序的示例。如果您有兴趣构建自己的自定义 Web 应用程序,这也是一个很好的起点。您可能想从最简单的 stream-js 示例开始。而 stream-app 基于 react.js 应用程序,要稍微复杂一点。

Web 界面及其 Web API 在与 VRED 交互时提供了全新的可能性。现在,我们可以轻松地构建完全自定义的流式传输应用程序,并在引擎基础上构建全新的工具。

好,今天就到这里。希望您喜欢我们的视频,下次见!


Python 代码示例

下面是教程 5:如何使用 Web 界面通过 Python 脚本远程控制 VRED 视频随附的 Python 脚本示例。

提示:

要下载这些压缩文件,请单击此处

custom-api.js

import { api } from 'http://localhost:8888/api.js';
            
// Connect event listener (signal)
api.vrAnnotationService.annotationsAdded.connect(() => console.log('Annotations added'));

// Add an annotation with name and text to the scene
function addAnnotation(name, text) {
    api.vrAnnotationService.createAnnotation(name)
    .then(() => {
        api.vrAnnotationService.findAnnotation(name)
        .then(a => a.setText(text));
    })
    .catch(() => console.error('Add annotation failed'));
}

// Get a list of all annotations
function getAnnotations() {
    api.vrAnnotationService.getAnnotations()
    .then(annotations => annotations.forEach(a => console.log(a)))
    .catch(() => console.error('Get annotations failed')); 
}

// Add both functions to the global scope
// So that we can call them from the html document
window.addAnnotation = addAnnotation;
window.getAnnotations = getAnnotations;

custom_web_interface_endpoints.html

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8"/>
        <script>
            // Send a generic python command without return value
            function sendPythonCommand(command) {
                var http = new XMLHttpRequest();
                var url = 'http://localhost:8888/python?value=' + encodeURI(command);
                http.open('GET', url, true);
                http.send();
            }

            // Send a python command with expecting a return value
            function sendPythonCommandWithReturnValue(command) {
                var http = new XMLHttpRequest();
                var url = 'http://localhost:8888/pythoneval2?value=' + encodeURI(command);
                http.open('GET', url, true);
                http.onreadystatechange = function() {
                    if (this.readyState == 4 && this.status == 200) {
                        var response = eval(this.responseText);
                        console.log(response);
                    }
                };
                http.send();
            }

            // Write a message to the terminal
            function logInfo(message) {
                sendPythonCommand("logInfo('"+message+"')");
            }

            // Select a variant set 
            function selectVariantSet(variantSet) {
                sendPythonCommand("selectVariantSet('"+variantSet+"')");
            }

            // get all viewpoints
            function getViewpoints() {
                sendPythonCommandWithReturnValue('getViewpoints()');
            }
        </script>
    </head>
    <body>
                <button onclick="logInfo('This is a message')">Print Info Message</button>

        <button onclick="selectVariantSet('Black Metallic')">Variant: Select Black Metallic</button>

        <button onclick="getViewpoints()">Request Viewpoints</button>
    </body>
</html>

custom_web_interface_web_api.html

<!DOCTYPE html>
<html>
    <head>
        <script type="module" src="custom-api.js"></script>
    </head>
    <body>
                <button onclick="addAnnotation('New Annotation', 'This is a new annotation')">Add annotation</button>
        <button onclick="getAnnotations()">Get all annotations</button>
    </body>
</html>