Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Question: Linking html resources #187

Closed
tjohnsonhvac opened this issue Mar 1, 2018 · 30 comments
Closed

Question: Linking html resources #187

tjohnsonhvac opened this issue Mar 1, 2018 · 30 comments
Labels

Comments

@tjohnsonhvac
Copy link

What is the correct way to link local js and ccs files?
It appears the standard html and <script src="script.js"></script>
do not work.
I also tried using the full path for both and that didn't work either.
And I tried ./style.css. that didn't work.

It does look like CDN links work but they take a long time to load.

<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

Any advice?

@r0x0r
Copy link
Owner

r0x0r commented Mar 1, 2018

Currently there is no smooth way to add linked resources to HTML loaded via load_html. #181 aims to alleviate that.
What you can do now though

  1. Inject CSS and JS directly into your HTML via Python's format or similar
  2. Load JS and CSS using evaluate_js. Google on how to load CSS using Javascript
  3. Serve everything using a local web server (the traditional way)

@tjohnsonhvac
Copy link
Author

Thanks. My electron comparable app is almost complete. @ a whopping 19kb!!!!
before pyinstaller.

@tjohnsonhvac
Copy link
Author

Any pointers on pyinstaller? the exe opens and runs but the dialogs don't work. i put the WebBrowserInterop.dll in the datas import of the spec and it is in the distro folder with the exe after it compiles it.
it runs fine when i run it from python console.

@tjohnsonhvac
Copy link
Author

tjohnsonhvac commented Mar 1, 2018

A quick example of how I got around the css&js issue:

index.html:

<!DOCTYPE html>
<html lang="">
<head>
    <meta charset="UTF-8">
    <title>Untitled Document</title>
	<meta name="Author" content=""/>
	<link rel="stylesheet" type="text/css" href="style.css">
	<script src="script.js"></script>
</head>
<body>



</body>
</html>

style.css:

body {
    background-color: blue;
}

script.js:

alert("javascript loaded");

Python:

import webview
import threading
import io
import sys


html_file_path = 'index.html'
html= ''


def read_css(css_file_path):
    try:
        with open(css_file_path) as f_obj:
            css = f_obj.read()
    except Exception as error:
        print("Error opening .css file.")
        print(error, type(error))
    else:
        return css

def read_js(js_file_path):
    try:
        with open(js_file_path) as f_obj:
            js = f_obj.read()
    except Exception as error:
        print("Error opening .js file.")
        print(error, type(error))
    else:
        return js


with open(html_file_path) as f_obj:
    lines = f_obj.readlines()
    for line in lines:
        if '<link' in line:
            link_parts = line.split(' ')
            for part in link_parts:
                if 'href' in part:
                    if 'http' in part:
                        html+= line
                    else:
                        css_file_path = part.replace('href="', '').replace('">', '').replace('"', '').replace('\n', '')
                        css = read_css(css_file_path)
                        html += '<style>\n'
                        html += css
                        html += '</style>\n'                    
                    
        elif '<script' in line:
            if 'src' in line:
                script_parts = line.split(' ')
                for part in script_parts:
                    if 'src' in part:
                        if 'http' in part:
                            html+= line
                        else:
                            js_file_path = part.replace('src="', '').replace('"></script>', '').replace('"', '').replace('\n', '')
                            js = read_js(js_file_path)
                            html += '<script>\n'
                            html += js
                            html += '</script>\n'                        
            else:
                html+= line
        else:
            html+= line
            
print (html)
                    
def create_app():
    webview.load_html(html)
    
    
if __name__ == '__main__':
    t = threading.Thread(target=create_app)
    t.start()

    
    webview.create_window('load js&css example', width=900, height=600, debug=True)

The resulting HTML which loads correctly in webview:

<!DOCTYPE html>
<html>
    <head lang="en">
    <meta charset="UTF-8">
    <title>Untitled Document</title>
    <meta name="Author" content=""/>
<style>
body {
    background-color: blue;
}
</style>
<script>
alert("javascript loaded");
</script>
</head>

<body>
</body>
</html>

@r0x0r
Copy link
Owner

r0x0r commented Mar 2, 2018

WebBrowserInterop.dll was introduced only recently, I haven't got a chance to investigate how to make it play with pyinstaller. pywebview looks for the dll in the lib directory under the current directory as the running script. I am not sure how paths would play out when frozen with pyinstaller.

If you get it working, please post the solution here.

@tjohnsonhvac
Copy link
Author

Will do.
Just to confirm, WebBrowserInterop.dll is what gives the dialog window function?

@tjohnsonhvac
Copy link
Author

I put together a nice example with Pure.css and JQuery this morning with the code above.
If anyone uses it and has problems let me know.

image

@tjohnsonhvac
Copy link
Author

I found a solution to pyinstaller issue. I the augments telling pyinstaller where to find the files and where to put them needed to be added.

I used a .bat file to run pyinstaller:

pyinstaller --noconfirm --log-level=DEBUG ^
    --add-data="index.html;." ^
    --add-data="webview/lib/WebBrowserInterop.dll;webview/lib" ^
    --add-data="webview/js/alert.js;webview/js" ^
    --add-data="webview/js/api.js;webview/js" ^
    --add-data="webview/js/npo.js;webview/js" ^
    --hidden-import=clr ^
    --manifest="manifest.xml" ^
    --version-file="file_version_info.txt" ^
    --debug ^
    testApp.py

I am having an issue with the .exe disappearing when I use the --noconsole setting but that may be an anti-virus issue. I'll try to test later on a different computer.
I have not tested using the --onefile setting.

@tjohnsonhvac
Copy link
Author

So with pyinstaller set to --onefile you have to manually copy in the folder with the html, css , and js files in it because the they are loaded in by the open file method.

image
I'm not sure how the WebBrowserInterop.dll is loaded in but it still works with the --onefile setting.

@r0x0r
Copy link
Owner

r0x0r commented Mar 3, 2018

Good job. This seems to be an answer to the onefile scenario. Could you give this a try while you are at it?
https://stackoverflow.com/questions/7674790/bundling-data-files-with-pyinstaller-onefile

@tjohnsonhvac
Copy link
Author

Ok, so that is a good fix for the onefile issue.
for the icon, I edited your logo in the pywebview- master to make the icon.
I went to https://www.icoconverter.com/ and converted the .png to a .ico I'm sure we can figure a way to do this ourselves eventually.
I added the icon to the .bat file(it is in the same dir as the js_api script):
favicon.ico

pyinstaller --noconfirm --log-level=DEBUG ^
    --onefile --noconsole ^
    --add-data="webview/lib/WebBrowserInterop.dll;webview/lib" ^
    --add-data="webview/js/alert.js;webview/js" ^
    --add-data="webview/js/api.js;webview/js" ^
    --add-data="webview/js/npo.js;webview/js" ^
    --hidden-import=clr ^
    --manifest="manifest.xml" ^
    --version-file="file_version_info.txt" ^
    --icon=favicon.ico ^
    js_api.py

I compiled it and here it is:
image

Next I used the solution provided in the stackoverflow post. it worked great.

Import os and add this function to you code:

""" Get absolute path to resource, works for dev and for PyInstaller """
def resource_path(relative_path):
    base_path = getattr(sys, '_MEIPASS', os.path.dirname(os.path.abspath(__file__)))
    return os.path.join(base_path, relative_path)

Then anytime you need to get something from the temp file created by the onefile exe, wrap it in the function:

html_file_path = resource_path('index.html')

It will then find it in the temp folder created. this also works if you run the script from a python IDE.

I will put together a complete example that can be used as a framework and put it up here.
I also just realized I need to add into the html import, a way to ignore links and scripts wrapped in <!-- -->

@tjohnsonhvac
Copy link
Author

Here is a zip file with the js_api example broken up into html/css/js files.
Run the pyinstall.bat to compile into a onefile exe.
I added to it the open file dialog to show that it works when compiled to onefile.
I also added in a way for the html import to ignore these:

<!--blablabla-->

and

<!---
blablabla
-->

it ignores all lines with and between <!-- and -->

so it will not work correctly for:
<!--blablabla--><something important>
or
<!---
blablabla
--><something important>
html will only be read on the next line after -->.

pytronium.zip

@r0x0r
Copy link
Owner

r0x0r commented Mar 7, 2018

Thanks!

Thinking of this problem now, I realised a better solution for js files would be to embed js code in respective .py files and export them as modules. The WebBrowserInterop.dll would still require extra treatment. Looks like a job for a pyinstaller hook. https://pythonhosted.org/PyInstaller/hooks.html

@r0x0r
Copy link
Owner

r0x0r commented Mar 7, 2018

I have converted js files to py modules in the pyinstaller-support branch.

@tjohnsonhvac
Copy link
Author

It would be nice to keep the js, css and image references in the html file so that it works as close to web coding as possible. This way it keeps it easy for web developers to use this as a native wrapper for a web app if they want to.

When you say embed js code into .py files are you talking just about the js files in webview/js or any js file referenced in the html?

It does look like a hook could be used to grab the referenced files in the html file and bring them into the compiled file or folder when pyinstaller is ran. Then i guess we would only have to reference the hook when running pyinstaller. Would this be a good solution for these files or is there a better way to do this?

@tjohnsonhvac
Copy link
Author

I have been working on an app that uses a bunch of HTML5 elements. When I run it in the webview they don’t appear correctly. Would upgrading IE on my computer fix this or does winforms not support html5?

I’m running Windows 7.

@r0x0r
Copy link
Owner

r0x0r commented Mar 8, 2018

Regarding the js files, my idea is to keep only the js files related to pywebview inside Python code. Javascript is put everywhere these days, so putting it inside py files seems to be apt. User's own assets should certainly be in their original form.

As for the renderer, the latest installed IE version is used, so in your case it would certainly help to upgrade to IE11. Unfortunately you cannot go past IE11 and Microsoft has no plans adding Edge support to WinForms or WPF. Support for CEF will certainly be needed if the situation continues to be like this. The problem is discussed in #15.

@tjohnsonhvac
Copy link
Author

I have IE11.
So after doing some reading, IE11 lacks support for a lot of things.
For reference, this is the score for IE11 on Win7: https://html5te.st/703bb03c1f70ff9e
If I put together js files to replace some of these missing functions, would you want to include them in the built in j/py modules?

@r0x0r
Copy link
Owner

r0x0r commented Mar 9, 2018 via email

@tjohnsonhvac
Copy link
Author

tjohnsonhvac commented Mar 10, 2018

Good point.
I found some good polyfills out there for some missing stuff. I spent the day today with an IE window and a chrome window open, working through the IE comparability issues with SVGs so they would display correctly... fun... got it working tho.

@tjohnsonhvac
Copy link
Author

I'm having issues with downloading the svg as a png. I rendered the svg to a canvas but when i try to convert the canvas to a dataURL I get a security error in IE11. I looked it up and it seems to be a huge problem for people. Works fine in Chrome but not IE. Do you have any recommendations on how this could be handled on the python side without an external program like inkscape?

@r0x0r
Copy link
Owner

r0x0r commented Mar 13, 2018

@tjohnsonhvac
Copy link
Author

Thanks for the reply but no luck there.
I was able to get it to work using 3 js libraries that somehow work around the IE security issue. then I used base64 to decode the dataurl in python and save it to a png.

@r0x0r
Copy link
Owner

r0x0r commented Mar 26, 2018

The solution for linked resources is being implemented in #181.
I am closing this issue.

@r0x0r r0x0r closed this as completed Mar 26, 2018
@suffa07
Copy link

suffa07 commented Apr 15, 2019

I just starting using pywebview to build a desktop app where I want to display results of completed software tests, and bugs discovered. However, I am quite fuzzy on displaying some statistical data completed with pychart on the application. What would be the best approach to display this chart in the pywebvie desktop app? I am using the pystronium model from tjohnsonhvac above. Would it be best to serialize the Python data and pass it to a Javascript function? Additionally, I have a total of three pages that I would like to navigate between, and I can't seem to navigate to any page other than the index.html. I have a js function that gets the id of the href link when clicked and send it to webview.load_html(HTML) function. Any thoughts on this? Thanks

@r0x0r
Copy link
Owner

r0x0r commented Apr 15, 2019 via email

@suffa07
Copy link

suffa07 commented Apr 15, 2019 via email

@r0x0r
Copy link
Owner

r0x0r commented Apr 17, 2019

Added information about application architecture
https://pywebview.flowrl.com/guide/architecture.html

@suffa07
Copy link

suffa07 commented Apr 17, 2019 via email

@suffa07
Copy link

suffa07 commented Apr 17, 2019 via email

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

4 participants