-2

I am accessing to my own robots via wi-fi using a third party router. I wrote a small application that automatically log-in to the router, navigate through the tab of the router interface all using JavaScript inside a QML application. The small problem I have is that I have to trigger a relays to turn on/off the robot and inside the router GUI I need to trigger a combobox as shown in the print screen below. After clicking on the I/O tab, the address of the router page is https://123.456.789.123:7878/admin/ACEmanagerX.html#:

realys

The corresponding html is also shown below:

html

EDITS

Now in order to replicate the problem I have I deployed a very small website https://comboboxtest.netlify.com/ that carries only one combobox with the same name and same choice I am trying to trigger.

The problem is that I can't find an easy ways to trigger the relays either on or off.

The expected behavior would be: if it is OFF (value 0) put it ON (value 1 which is Drive Active Low) and vice versa.

If you copy/paste the code below and run it it will automatically open the website but, unfortunately, it does not change the combobox from the OFF position to Drive Active Low and vice versa.

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"
        }
}

What I tried so far

1) After going through this post I found out that it is very important to give time to the http request to start. That is why I added interval: 20000 after entering in the I/O tab. However, that was not useful and the combobox wasn't triggered.

2) I used this source to help me figure out how to trigger the combobox via JavaScript and that is exactly what I applied but I didn't see any value changed in the combobox.

3) I found this post which is very close to what I am doing, with the difference that here it was also used a Button to trigger the combobox. But in my case I have no button.

I know I am close to have it working but there is something I am missing. Thank you for shedding light on this matter for solving the problem

Emanuele
  • 2,194
  • 6
  • 32
  • 71
  • 1
    Some points... first you're mixing multiple unrelated problems into one question/issue, which would be easier to handle separately. 2nd, your MRE isn't one as there are obvious errors and missing parts, plus no way to recreate this w/out writing a custom HTML page. It's hard to tell what is an actual mistake or just stuff you cut out. I see a bunch of possible issues, the most obvious of which is that `document.getElementById('859-2-2')` will not work since there is no element by that ID -- that is the element _name_. Use `document.getElementsByName('859-2-2')[0]`. – Maxim Paperno Nov 21 '19 at 01:18
  • See https://stackoverflow.com/questions/51810358/how-to-quit-the-c-application-in-qt-qml for exiting an app programmatically from within the QML. – Maxim Paperno Nov 21 '19 at 01:30
  • @MaximPaperno, thanks for reading the question. I updated the question on how to handle a combobox via `JavaScript` and will write a separate question for the second part. – Emanuele Nov 21 '19 at 13:52
  • @MaximPaperno, I tried the modification `document.getElementsByName('859-2-2')[0];` but I still see no change in triggering the comboBox. What am I missing in the `JavaScript` function that I don't see? – Emanuele Nov 21 '19 at 13:54
  • Well... do you get errors... warnings? Anything in the output console? Do you know where it "dies?" Like... does time3 fire? `console.log()` some debug info from both QML and JS side. And what is `.arg(cBox)` doing? I don't see any placeholder in the text. _Always_ make sure you view console output while testing code, there can be many useful warnings in there. (In QtCreator that means watching "Application Output" for C++ or "General Messages" for `qmlscene`.) – Maxim Paperno Nov 21 '19 at 14:03
  • 1
    @MaximPaperno, following your advice I did this to make the question much more simple: I created and deployed a small website [https://comboboxtest.netlify.com/](https://comboboxtest.netlify.com/) and revised the code I posted. there is the section **EDITS** which I think is simpler now. Now if you run exactly that code it will automatically open the website but, unfortunately it still does not trigger the combobox. There is also the possibility to `inspect the element` and see the `html` page – Emanuele Nov 21 '19 at 15:31

1 Answers1

1

I came up with a working example for you. There is nothing tricky here really, just precise syntax, learning JavaScript, and especially paying attention to the warning messages Qt produces while running. I can't stress the latter enough -- if you can't see the console output, it's not even worth testing. Even the latest cleaned-up MRE generated console warnings which were easily remedied, after which the real issues became more apparent.

There are two primary changes. First the test for

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

was never true. Pretty basic and easily determined with a console.log(). Good way to compare URL to a string is with url.toString().

Other big change is the script which runs in the Web View/browser. I tried to annotate this a bit. Hopefully I understood the purpose correctly -- toggle the selection in the combo box to the opposite of what is currently selected, and fire the change event. I'm not entirely sure what the original script code was trying to do (or why the index change was inside the even listener... it would never get changed that way?). Anyway, here's what my version looks like.

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
  }
}
Maxim Paperno
  • 4,485
  • 2
  • 18
  • 22
  • thank you very much for the example and for your time. That was a great and simple way to show how the process works. Sometimes I get stuck with simple things. It would just be enough to `console.log` the output! Thank you very much again! :) – Emanuele Nov 21 '19 at 19:47