Browser inside task panel

Here's the place for discussion related to coding in FreeCAD, C++ or Python. Design, interfaces and structures.
Forum rules
Be nice to others! Respect the FreeCAD code of conduct!
wmayer
Founder
Posts: 20243
Joined: Thu Feb 19, 2009 10:32 am
Contact:

Re: Browser inside task panel

Post by wmayer »

For me this seems to work without problems. The only thing I realized is an error message:
Failed to set referrer policy: The value 'strict-origin-when-cross-origin' is not one of 'no-referrer', 'no-referrer-when-downgrade', 'origin', 'origin-when-cross-origin', or 'unsafe-url'. The referrer policy has been left unchanged.
OS: Ubuntu 18.04.5 LTS (XFCE/xubuntu)
Word size of OS: 64-bit
Word size of FreeCAD: 64-bit
Version: 0.19.22436 +2 (Git)
Build type: Debug
Branch: pr3757
Hash: 1c730a7f00c8ed066dc9857e1e637cf1c8ea3210
Python version: 3.6.9
Qt version: 5.9.5
Coin version: 4.0.0a
OCC version: 7.3.0
Locale: German/Germany (de_DE)
User avatar
Chris_G
Veteran
Posts: 2579
Joined: Tue Dec 31, 2013 4:10 pm
Location: France
Contact:

Re: Browser inside task panel

Post by Chris_G »

It worked fine for me. No error at all.
Only thing, is that the browser is not in the combo task panel , but in a new panel.
I am typing from this panel.

Code: Select all

OS: Manjaro Linux (KDE//usr/share/xsessions/plasma)
Word size of OS: 64-bit
Word size of FreeCAD: 64-bit
Version: 0.19.22315 +107 (Git)
Build type: Release
Branch: code_duplic
Hash: 0e2494d8963ea71abaeb41117daeb330e95da327
Python version: 3.8.5
Qt version: 5.15.0
Coin version: 4.0.0
OCC version: 7.4.0
Locale: French/France (fr_FR)
wmayer
Founder
Posts: 20243
Joined: Thu Feb 19, 2009 10:32 am
Contact:

Re: Browser inside task panel

Post by wmayer »

But with the Windows build I can confirm the issue. No matter how I activate the password field the key events are handled as shortcuts. Maybe the behaviour depends on the Qt version because so far it works with 5.9.5, 5.15.0 but fails with 5.12.1. However, it can also be specific to Windows because only there the issue has been reproduced.

OS: Windows 10 (10.0)
Word size of OS: 64-bit
Word size of FreeCAD: 64-bit
Version: 0.19.22417 (Git)
Build type: Release
Branch: master
Hash: f681b86abdddd55e2dcb80bc4612251570cc9b8b
Python version: 3.6.8
Qt version: 5.12.1
Coin version: 4.0.0a
OCC version: 7.3.0
Locale: English/Germany (en_DE)
wmayer
Founder
Posts: 20243
Joined: Thu Feb 19, 2009 10:32 am
Contact:

Re: Browser inside task panel

Post by wmayer »

Here is a simple solution to fix the problem. The idea is to override the event() function and require all key events to be sent to the dialog if it has the focus:

Code: Select all

import FreeCADGui
from PySide2 import QtCore, QtWidgets, QtWebEngineWidgets
class TransWindow(QtWidgets.QDialog):
    def __init__(self):
        super(TransWindow, self).__init__()
        self.webView = QtWebEngineWidgets.QWebEngineView()
        self.webView.setSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding)
        self.layout = QtWidgets.QVBoxLayout(self)
        self.layout.addWidget(self.webView)
    def showEvent(self, event):
        super(TransWindow, self).showEvent(event)
        self.webView.load(QtCore.QUrl("https://forum.freecadweb.org/ucp.php?mode=login"))
    def event(self, event):
        if event.type() == QtCore.QEvent.ShortcutOverride:
            event.accept()
        return super(TransWindow, self).event(event)

mw = FreeCADGui.getMainWindow()
d = QtWidgets.QDockWidget()
d.setWidget(TransWindow())
mw.addDockWidget(QtCore.Qt.RightDockWidgetArea, d)
User avatar
bernd
Veteran
Posts: 12849
Joined: Sun Sep 08, 2013 8:07 pm
Location: Zürich, Switzerland
Contact:

Re: Browser inside task panel

Post by bernd »

same here on Linux works like a sharm, on windows no matter if LibPack or Conda version is used the problem exist.

Code: Select all

OS: Debian GNU/Linux 10 (buster) (KDE//usr/share/xsessions/plasma)
Word size of OS: 64-bit
Word size of FreeCAD: 64-bit
Version: 0.19.22372 (Git)
Build type: Unknown
Branch: master
Hash: 3bdb812a7d8ba2685be0133e842d6281a0f0c20b
Python version: 3.7.3
Qt version: 5.11.3
Coin version: 4.0.0a
OCC version: 7.3.0
Locale: German/Switzerland (de_CH)
User avatar
bernd
Veteran
Posts: 12849
Joined: Sun Sep 08, 2013 8:07 pm
Location: Zürich, Switzerland
Contact:

Re: Browser inside task panel

Post by bernd »

wmayer wrote: Wed Sep 16, 2020 6:23 am Here is a simple solution to fix the problem. The idea is to override the event() function and require all key events to be sent to the dialog if it has the focus:
works great here on windows
User avatar
Kunda1
Veteran
Posts: 13434
Joined: Thu Jan 05, 2017 9:03 pm

Re: Browser inside task panel

Post by Kunda1 »

This together with Pinger macro would be sweet.
Alone you go faster. Together we go farther
Please mark thread [Solved]
Want to contribute back to FC? Checkout:
'good first issues' | Open TODOs and FIXMEs | How to Help FreeCAD | How to report Bugs
User avatar
bernd
Veteran
Posts: 12849
Joined: Sun Sep 08, 2013 8:07 pm
Location: Zürich, Switzerland
Contact:

Re: Browser inside task panel

Post by bernd »

The code here is the base for this macro https://www.forum.freecadweb.org/viewtopic.php?p=451865 This macro has been working during dev cycle of 0.19, but crashes on release version 0.19.1 and 0.19.2 as well as 0.20 dev ...

Find attached a simplified version which shows the problem and FreeCAD crashes

import_part_from_web_crash.FCMacro
(4.97 KiB) Downloaded 40 times

Code: Select all

import os
import tempfile
import threading
from urllib.request import urlretrieve
from PySide2 import QtCore
from PySide2 import QtWidgets
from PySide2 import QtWebEngineWidgets
from PySide2 import QtWebChannel

import FreeCAD
import FreeCADGui
import Part


the_html_code = """
<html>
<head>
    <script src="qrc:///qtwebchannel/qwebchannel.js"></script>
</head>
<header><title>title</title></header>
<body>

<p id="output"></p>

<h2><a href="#" onclick="jsWebPartImporter.text('https://forum.freecadweb.org/download/file.php?id=48016')">Click to import a Part from Internet into FreeCAD</a></h2>
<h2><a href="#" onclick="alert('Javascript works!')">Click for Java Script Test</a></h2>

<script type="text/javascript">
    window.onload = function() {
        new QWebChannel(qt.webChannelTransport, function (channel) {
            window.jsWebPartImporter = channel.objects.jsWebPartImporter;
            console.log(jsWebPartImporter);
        });
    }
</script> 
</body>
</html>
"""


class WebPartImporter(QtCore.QObject):
    def __init__(self, parent=None):
        super(WebPartImporter, self).__init__(parent)

    @QtCore.Slot(str)
    def text(self, url_to_step_file):

        print(url_to_step_file)

        # get a temp file with ending step
        tmpstepfile_path = tempfile.NamedTemporaryFile(suffix=".step").name
        print(tmpstepfile_path)

        # download the step file
        urlreturn = urlretrieve(url_to_step_file, tmpstepfile_path)
        print(urlreturn[0])

        # read the step (the read method needs file ending step)
        # and add it to the active document
        # create a new doc if there is no active doc
        Part.show(Part.read(tmpstepfile_path))
        os.remove(tmpstepfile_path)
        print(FreeCAD.ActiveDocument.Name)

        # set the view
        FreeCADGui.ActiveDocument.activeView().viewAxonometric()
        FreeCADGui.SendMsgToActiveView("ViewFit")


class BrowserWidget(QtWidgets.QDialog):
    def __init__(self):
        super(BrowserWidget, self).__init__()
        self.web_view = QtWebEngineWidgets.QWebEngineView()

        channel = QtWebChannel.QWebChannel(self)
        channel.registerObject("jsWebPartImporter", WebPartImporter(self.web_view))
        self.web_view.page().setWebChannel(channel)

        # layout 
        self.web_view.setSizePolicy(
            QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding
        )
        self.layout = QtWidgets.QVBoxLayout(self)
        self.layout.addLayout(QtWidgets.QHBoxLayout())
        self.layout.addWidget(self.web_view)
        
        self.thread = threading.Thread(target=self.runJs, daemon=True)
        self.thread.start()

    def runJs(self):
        self.web_view.page().runJavaScript("(function(){ alert('goodbye'); })();")
    
    def showEvent(self, event):
        super(BrowserWidget, self).showEvent(event)
        self.web_view.setHtml(the_html_code)


mw = FreeCADGui.getMainWindow()
d = QtWidgets.QDockWidget()
d.setWidget(BrowserWidget())
mw.addDockWidget(QtCore.Qt.RightDockWidgetArea, d)


OS: Windows 10 Version 2009
Word size of OS: 64-bit
Word size of FreeCAD: 64-bit
Version: 0.19.24291 (Git)
Build type: Release
Branch: releases/FreeCAD-0-19
Hash: 7b5e18a0759de778b74d3a5c17eba9cb815035ac
Python version: 3.8.6+
Qt version: 5.15.2
Coin version: 4.0.1
OCC version: 7.5.0
Locale: German/Switzerland (de_CH)

OS: Debian GNU/Linux 10 (buster) (KDE//usr/share/xsessions/plasma)
Word size of FreeCAD: 64-bit
Version: 0.20.25028 (Git)
Build type: Unknown
Branch: femtmp
Hash: 130a0e13eac8dc11e5c6edc22aa35a35b50c4249
Python version: 3.7.3
Qt version: 5.11.3
Coin version: 4.0.0a
OCC version: 7.3.0
Locale: German/Switzerland (de_CH)
tobias1
Posts: 1
Joined: Tue Jun 08, 2021 6:33 am

Re: Browser inside task panel

Post by tobias1 »

Hi,

I'm the dev behind the 3DfindIT plugin and discovered the issue above yesterday. Additionally to what Bernd said, the problem only occurs when executing JavaScript from a side-thread:

Code: Select all

        self.thread = threading.Thread(target=self.runJs, daemon=True)
        self.thread.start()

    def runJs(self):
        self.web_view.page().runJavaScript("(function(){ alert('goodbye'); })();")
This worked fine during the development cycle, but no longer does. The last version I can verify it worked is this one:

OS: Windows 10 (10.0)
Word size of OS: 64-bit
Word size of FreeCAD: 64-bit
Version: 0.19.22988 (Git)
Build type: Release
Branch: master
Hash: cdc01cb20cfa74079c45b9dc2dab81e3248b2aa5
Python version: 3.6.8
Qt version: 5.12.1
Coin version: 4.0.0a
OCC version: 7.3.0
Locale: German/Germany (de_DE)

As a quick fix I can change my code to wait for tasks in a side-thread, but actually execute them on the main-thread. In the sample above: instead of executing the the JavaScript immediately, raise an event and execute the JavaScript when handling the event. Here's the code from above changed to work with events:

Code: Select all

# ***************************************************************************
# *   Copyright (c) 2020 Bernd Hahnebach <bernd@bimstatik.org>              *
# *                                                                         *
# *   This file is part of the FreeCAD CAx development system.              *
# *                                                                         *
# *   This program is free software; you can redistribute it and/or modify  *
# *   it under the terms of the GNU Lesser General Public License (LGPL)    *
# *   as published by the Free Software Foundation; either version 2 of     *
# *   the License, or (at your option) any later version.                   *
# *   for detail see the LICENCE text file.                                 *
# *                                                                         *
# *   This program is distributed in the hope that it will be useful,       *
# *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
# *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
# *   GNU Library General Public License for more details.                  *
# *                                                                         *
# *   You should have received a copy of the GNU Library General Public     *
# *   License along with this program; if not, write to the Free Software   *
# *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  *
# *   USA                                                                   *
# *                                                                         *
# ***************************************************************************

# forum topic
# https://forum.freecadweb.org/viewtopic.php?f=10&t=25189

# very helpful links
# https://stackoverflow.com/questions/51011244/pyside2-and-supporting-addtojavascriptwindowobject
# https://stackoverflow.com/questions/39544089/how-can-i-access-python-code-from-javascript-in-pyqt-5-7/42740287


import os
import tempfile
import threading
from urllib.request import urlretrieve
from PySide2 import QtCore
from PySide2 import QtWidgets
from PySide2 import QtWebEngineWidgets
from PySide2 import QtWebChannel

import FreeCAD
import FreeCADGui
import Part


the_html_code = """
<html>
<head>
    <script src="qrc:///qtwebchannel/qwebchannel.js"></script>
</head>
<header><title>title</title></header>
<body>

<p id="output"></p>

<h2><a href="#" onclick="jsWebPartImporter.text('https://forum.freecadweb.org/download/file.php?id=48016')">Click to import a Part from Internet into FreeCAD</a></h2>
<h2><a href="#" onclick="alert('Javascript works!')">Click for Java Script Test</a></h2>

<script type="text/javascript">
    window.onload = function() {
        new QWebChannel(qt.webChannelTransport, function (channel) {
            window.jsWebPartImporter = channel.objects.jsWebPartImporter;
            console.log(jsWebPartImporter);
        });
    }
</script> 
</body>
</html>
"""


class WebPartImporter(QtCore.QObject):
    def __init__(self, parent=None):
        super(WebPartImporter, self).__init__(parent)

    @QtCore.Slot(str)
    def text(self, url_to_step_file):

        print(url_to_step_file)

        # get a temp file with ending step
        tmpstepfile_path = tempfile.NamedTemporaryFile(suffix=".step").name
        print(tmpstepfile_path)

        # download the step file
        urlreturn = urlretrieve(url_to_step_file, tmpstepfile_path)
        print(urlreturn[0])

        # read the step (the read method needs file ending step)
        # and add it to the active document
        # create a new doc if there is no active doc
        Part.show(Part.read(tmpstepfile_path))
        os.remove(tmpstepfile_path)
        print(FreeCAD.ActiveDocument.Name)

        # set the view
        FreeCADGui.ActiveDocument.activeView().viewAxonometric()
        FreeCADGui.SendMsgToActiveView("ViewFit")


class BrowserWidget(QtWidgets.QDialog):
    hasWork = QtCore.Signal(str)
  
    def __init__(self):
        super(BrowserWidget, self).__init__()
        self.web_view = QtWebEngineWidgets.QWebEngineView()

        channel = QtWebChannel.QWebChannel(self)
        channel.registerObject("jsWebPartImporter", WebPartImporter(self.web_view))
        self.web_view.page().setWebChannel(channel)

        # layout 
        self.web_view.setSizePolicy(
            QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding
        )
        self.layout = QtWidgets.QVBoxLayout(self)
        self.layout.addLayout(QtWidgets.QHBoxLayout())
        self.layout.addWidget(self.web_view)
        
        self.hasWork.connect(self._hasWork)
        
        self.thread = threading.Thread(target=self.runJs, daemon=True)
        self.thread.start()

    def runJs(self):
        self.hasWork.emit("(function(){ alert('goodbye'); })();")
        
    def _hasWork(self, script):
      self.web_view.page().runJavaScript(script)
    
    def showEvent(self, event):
        super(BrowserWidget, self).showEvent(event)
        self.web_view.setHtml(the_html_code)


mw = FreeCADGui.getMainWindow()
d = QtWidgets.QDockWidget()
d.setWidget(BrowserWidget())
mw.addDockWidget(QtCore.Qt.RightDockWidgetArea, d)
Please give me Feedback if I should fix this on my side or if that's something that will be fixed in the core.
ABeton
Posts: 150
Joined: Tue Sep 03, 2019 6:39 pm

Re: Browser inside task panel

Post by ABeton »

I just saw this thread and it seems very interesting, but I am not sure I understand the best what the macro does. So it opens a file from the web, which is then placed into a temp file and imports it into FreeCAD, is that correct?

I would like to implement this or a similar system either to the Parts library macro, or the Objects library in the BIM workbench. So it would be possible to both open and save files from sites like GrabCAD directly to the parts library on the local computer. Would this be difficult to do?

EDIT: I just checked, and this option is already available :oops:
Post Reply