首页 > 解决方案 > Qt5-QML:如何通过 JavaScript 处理 QML 中的组合框

问题描述

我正在使用第三方路由器通过 wi-fi 访问我自己的机器人。我写了一个小应用程序,它可以自动登录到路由器,通过tab路由器界面导航,所有这些都在应用程序JavaScript内部QML使用。我遇到的一个小问题是我必须触发一个继电器来打开/关闭机器人,并且在路由器 GUI 内部我需要触发一个combobox,如下面的打印屏幕所示。点击I/O选项卡后,路由器页面的地址为https://123.456.789.123:7878/admin/ACEmanagerX.html#

真的

对应html的也如下图:

html

编辑

现在,为了复制这个问题,我部署了一个非常小的网站https://comboboxtest.netlify.com/,它只包含一个我试图触发的具有相同名称和相同选择的组合框。

问题是我找不到触发继电器开启或关闭的简单方法。

预期的行为将是:如果它是OFF(值 0),则放置它ON(值 1,即 Drive Active Low),反之亦然。

如果您复制/粘贴下面的代码并运行它,它将自动打开网站,但不幸的是,它不会将组合框从OFF位置更改为Drive Active Low,反之亦然。

import QtQuick 2.13
import QtQuick.Controls 2.13
import QtWebEngine 1.9

ApplicationWindow{
    id: root
    width: 1940
    height: 1100
    visible: true
    property string cBox: "https://comboboxtest.netlify.com"
    QtObject {
        // Below property triggers the combo box of the relays evaluating the normally closed (NC) or
        // normally opened (NO) circuit
        property string get_normallyClosed_or_normallyOpened_comboBox: "
            var comboBox = document.getElementsByName('859-2-2')[0];
            comboBox.addEventListener('change', function (e) {
                comboBox.options[0].selected = true;
                if ('createEvent' in document) {
                    var evt = document.createEvent('HTMLEvents');
                    evt.initEvent('change', false, true);
                    sel.dispatchEvent(evt);
                }
                else {
                    sel.fireEvent('onchange');
                }
            });
            sel.addEventListener('change', function (e) {
            alert('changed');
            });
        ".arg(cBox)
    }

    Timer {
         id: timer
         interval: 1000; repeat: false
         onTriggered: view.runJavaScript(internals.get_normallyClosed_or_normallyOpened_comboBox)
        }

        WebEngineView {
            id: view
            anchors.fill: parent
            onUrlChanged: {
                console.log(url)
                if(url === Qt.resolvedUrl("https://comboboxtest.netlify.com/"))
                    timer.running = true
            }
            onCertificateError: function(error) {
                error.ignoreCertificateError();
            }
            Component.onCompleted: view.url = "https://comboboxtest.netlify.com"
        }
}

到目前为止我尝试了什么

1)看完这篇文章后,我发现给http请求启动时间是非常重要的。这就是我interval: 20000在输入I/O选项卡后添加的原因。但是,这没有用,也没有触发组合框。

2)我使用这个源来帮助我弄清楚如何触发组合框JavaScript,这正是我应用的,但我没有看到组合框中的任何值发生变化。

3)我发现这篇文章与我正在做的非常接近,不同之处在于它在这里也被用来Button触发组合框。但就我而言,我没有按钮。

我知道我已经接近让它工作了,但是我缺少一些东西。感谢您为解决问题而阐明此事

标签: javascriptqtc++11qmlqt5

解决方案


我为你想出了一个可行的例子。这里真的没有什么棘手的,只是精确的语法,学习 JavaScript,特别注意 Qt 在运行时产生的警告消息。我对后者的压力再怎么强调也不为过——如果你看不到控制台的输出,那么它甚至不值得测试。即使是最新清理的 MRE 也会生成控制台警告,这些警告很容易解决,之后真正的问题变得更加明显。

有两个主要变化。首先测试

if (url === Qt.resolvedUrl("https://comboboxtest.netlify.com/"))

从来没有true。非常基本,很容易用console.log(). 将 URL 与字符串进行比较的好方法是使用url.toString().

另一个重大变化是在 Web 视图/浏览器中运行的脚本。我试着注释一下。希望我正确理解了目的——将组合框中的选择切换到与当前选择相反的位置,然后触发change事件。我不完全确定原始脚本代码试图做什么(或者为什么索引更改在偶数侦听器内部......它永远不会那样改变?)。无论如何,这就是我的版本的样子。

import QtQuick 2.13
import QtQuick.Controls 2.13
import QtWebEngine 1.9

ApplicationWindow{
  id: root
  width: 320
  height: 200
  visible: true
  property string url: "https://comboboxtest.netlify.com/"

  QtObject {
    id: internals
    // Below property triggers the combo box of the relays evaluating the normally closed (NC) or
    // normally opened (NO) circuit
    property string get_normallyClosed_or_normallyOpened_comboBox: "
      var comboBox = document.getElementsByName('859-2-2')[0];
      // Check that we found a target element.
      if (comboBox) {
        // Add event listener to verify that combo change event gets fired.
        comboBox.addEventListener('change', function (e) {
          console.log('Change event detected:', e);
        });

        // Get new option index by flipping the current selected index
        var newIdx = (comboBox.selectedIndex + 1) % 2;
        console.log(comboBox, comboBox.selectedIndex, newIdx);  // debug
        // set the new index
        comboBox.selectedIndex = newIdx;
        // fire change event
        comboBox.dispatchEvent(new Event('change'));
      }
      else {
        console.error('comboBox not found!');
      }
    ";
  }

  Timer {
    id: timer
    interval: 1000; repeat: false
    onTriggered: {
      console.log("Triggered timer with ", view.url);
      view.runJavaScript(internals.get_normallyClosed_or_normallyOpened_comboBox);
    }
  }

  WebEngineView {
    id: view
    anchors.fill: parent
    onUrlChanged: {
      if (url.toString() === root.url)
        timer.running = true
    }
    onCertificateError: function(error) {
      error.ignoreCertificateError();
    }
    Component.onCompleted: view.url = root.url
  }
}

推荐阅读