Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

Uncaught ReferenceError by using QWebEngineView in Qt 5.15.1



  • I am using QWebEngineView to display plotly / React widgets in a Qt application.
    I use QWebChannel to communicate between C++ code and JavaScript code.
    It was running OK with Qt 5.9.4 version but since we moved last week to Qt 5.15.1 version the plots randomly don’t appear (empty QWebEngineView widget) with this following kind of message:
    [26752:48980:1125/134129.765:INFO:CONSOLE(132)] "Uncaught ReferenceError: DataHistogram is not defined", source: qrc:///data_histogram.html (132)
    OR
    js: Uncaught ReferenceError: DataPlotProxy is not defined
    [32328:26544:1125/140044.220:INFO:CONSOLE(16)] "Uncaught ReferenceError: DataPlotProxy is not defined", source: qrc:///data_histogram.js (16)

    It randomly failed with a Release version (~ 1/ 2) but always fail with a Debug version.

    In the html file displayed in the QWebEngineView we load several javascript files and it seems that when we execute the main javascript, the others are not always loaded, which generates the error message.

    I have reproduced the issue in a small example working OK with Qt 5.9.4 but failing with Qt 5.15.1.

    Here is the main:

    int main(int argc, char *argv[])
    {
        QApplication a(argc, argv);
    
        QDialog* dialog = new QDialog (nullptr);
    
        QVBoxLayout* vertLayout = new QVBoxLayout (dialog);
        QDialogButtonBox* buttonBox = new QDialogButtonBox (QDialogButtonBox::Close, dialog);
    
        QWebEngineSettings::globalSettings ()->setAttribute (QWebEngineSettings::AutoLoadImages, true);
        QWebEngineSettings::globalSettings ()->setAttribute (QWebEngineSettings::PluginsEnabled, true);
        QWebEngineSettings::globalSettings ()->setAttribute (QWebEngineSettings::JavascriptEnabled, true);
    
        QWebEngineView* web_view = new QWebEngineView (dialog);
        web_view->setMinimumHeight (500);
        web_view->setMinimumWidth (700);
        vertLayout->addWidget (web_view);
        vertLayout->addWidget (buttonBox);
        dialog->connect (buttonBox, SIGNAL (rejected ()), dialog, SLOT (reject ()));
        dialog->resize (QSize (1200, 800));
    
        pdgm::plot_example::DataResult result;
        result.plot_title = "The example";
        result.data_vector = {1, 2, 5, 6, 8, 9, 7};
    
        pdgm::plot_example::DataPlotProxy* proxy = new pdgm::plot_example::DataPlotProxy  (web_view);
    
        QWebChannel* channel = new QWebChannel (web_view->page ());
        web_view->page ()->setWebChannel (channel);
        channel->registerObject (QString ("model"), proxy);
    
        web_view->show ();
        web_view->setUrl (QUrl ("qrc:///data_histogram.html"));
    
        dialog->show();
        return a.exec();
    }
    

    Here is the html file:

    <!DOCTYPE html>
    <html lang="en">
    <!--
        * Copyright 2020 Emerson Paradigm Holding LLC. All rights reserved.
        *
        * Use of this software is subject to the terms and conditions of the license
        * agreement with Emerson Paradigm Holding LLC and/or its affiliates and
        * subsidiaries (collectively "Paradigm").
    -->
    <head>
        <meta charset="UTF-8">
        <script src="qrc:///babel.js"></script>
        <script src="qrc:///react.js"></script>
        <script src="qrc:///react-dom.js"></script>
        <script src="qrc:///create-plotly-component.js"></script>
        <script src="qrc:///plotly.min.js"></script>
        <script src="qrc:///qwebchannel.js"></script>
        <script src="qrc:///material-ui.js"></script>
        <script src="qrc:///jstat.min.js"></script> <!-- used to compute ranges -->
        <title>Data Histogram</title>
        <style>
            #outer-div {
                display: flex;
                flex-flow: row;
                width: 100%;
                height: 100%;
                flex-grow: 1;
                overflow: auto; /* Show the scrollbars if needed */
                margin: 0;
                padding: 0;
            }
    
            #plots-div,
            #plots2-div {
                min-width: 400px;
                min-height: 200px;
                flex: 1;
                display: flex;
                flex-direction: row;
                overflow-x: auto; /* Show the horizontal scrollbar if needed */
                overflow-y: hidden; /* Hide the vertical scrollbar */
                white-space: nowrap;
                margin: 0;
                padding: 0;
            }
    
            html,
            body {
                height: 100%;
                overflow: hidden; /* Hide the scrollbars */
                white-space: nowrap;
                margin: 0;
                padding: 0;
            }
    
            body {
                display: flex;
                flex-flow: column;
                background-color:white;
            }
        </style>
    </head>
    
    <body>
        <script type="text/babel" src="qrc:///DataPlotProxy.js"></script>
        <script type="text/babel" src="qrc:///DataHistogram.js"></script>
    
        <div id='outer-div'>
            <div id="plots-div"></div>
        </div>
        <script type="text/babel">
            const {
            List,
            ListItem,
            ListItemIcon,
            ListItemText,
            Switch
            } = window['material-ui'];
        </script>
        <script src="qrc:///data_histogram.js"></script>
    </body>
    </html>
    

    Here is the main javascript file:

    var exports = {};
    
    window.onload = function () {
        new QWebChannel (qt.webChannelTransport, function (channel) {
            viewPlot (channel.objects.model);
        });
        function viewPlot(cpp_proxy_model) {
            // create a proxies that wrap the c++ model and provide API to access to data
            let pp_proxy = new DataPlotProxy(cpp_proxy_model);
            pp_proxy.renderHistogram ('plots-div');
        }
    }
    

    Extract of the DataPlotProxy class:

    class DataPlotProxy {
        constructor(cpp_proxy_model) {
            this.proxy = cpp_proxy_model;
    …
        }
    …
    
        renderHistogram (plotly_div) {
            this.plotly_div = plotly_div;
    
            ReactDOM.render (<DataHistogram model = {this} division = {plotly_div} />, document.getElementById(plotly_div));
        }
    }
    export default DataPlotProxy;
    

    Extract of the DataHistogram class:

    const PlotlyComponent = createPlotlyComponent(Plotly);
    
    class DataHistogram extends React.Component {
        constructor(props, context) {
            super(props, context);
    …
            this.state = {
                model: null,   // model is the DataPlotProxy
                data: null,     
                layout: null
            };
        }
    …
        render() {
            let {model, data, layout} = this.state;
    
            if (model == null) {
                // the component is has not yet finished to mount
                return ( <PlotlyComponent/> );
            }
    
            let config = {
                displaylogo: false,
                displayModeBar: false
            };
            let plot = (<PlotlyComponent
                         useResizeHandler = {true}
                         data = {data}
                         layout = {layout}
                         config = {config}
    />);
    
            return plot;
        }
    }
    

    I have tried several fixes but with no full success.

    The first try was to add async=false in html file to try to force the load of the js files synchronously:

    <script type="text/babel" src="qrc:///DataPlotProxy.js" async=”false”></script>
    <script type="text/babel" src="qrc:///DataHistogram.js" async=”false”></script>
    

    It was better but the issue still happens randomly (~ 1/10 in Release and ~1/2 in Debug).

    I have also tried to execute the main JavaScript code on the load of the last other js files but here I face to another issue:
    I have tried this code found on the web:

    // RECURSIVE LOAD SCRIPTS
    function load_scripts( urls, final_callback, index=0 )
    {
        if( typeof urls[index+1] === "undefined" )
        {
            load_script( urls[index], final_callback );
        }
        else
        {
            load_script( urls[index], function() {
                load_scripts( urls, final_callback, index+1 );
            } );
        }
    }
    
    // LOAD SCRIPT
    function load_script( url, callback )
    {
        var script = document.createElement( "script" );
        script.type = "text/javascript";
         script.onload = function() { callback(); };
        script.src = url;
        document.getElementsByTagName( "head" )[0].appendChild( script );
    }
    
    // Main Script  
    var view_plot = function()
    {
        new QWebChannel (qt.webChannelTransport, function (channel) {
            viewPlot (channel.objects.model);
        });
        function viewPlot(cpp_proxy_model) {
            // create a proxies that wrap the c++ model and provide API to access to data
            let pp_proxy = new DataPlotProxy(cpp_proxy_model);
            pp_proxy.renderHistogram ('plots-div');
        }
    }
    
    window.onload = function () {
        var js = [ "qrc:///DataPlotProxy.js", "qrc:///DataHistogram.js" ];
        load_scripts( js, view_plot );
    }
    

    But it works only with script.type = "text/javascript" and not with script.type ="text/babel"
    And unfortunately in our application we have very complex javascript files creating react widgets and we need to use babel.

    We have also tried to use Qt 5.15.2. It works for the simple example but not for a more complex one with more JavaScript files to load and more plots to display.


Log in to reply