r0x0r / pywebview

Build GUI for your Python program with JavaScript, HTML, and CSS
https://pywebview.flowrl.com
BSD 3-Clause "New" or "Revised" License
4.76k stars 554 forks source link

Open Custom File Extension (MacOS CFBundleTypeExtensions) #569

Open jmitchel3 opened 4 years ago

jmitchel3 commented 4 years ago

Specification

Description

First off. This is an amazing project. Thank you for creating pywebview!!

An event handler for when a file is opened for a specific file extension/type in macOS I believe it's CFBundleTypeExtensions in the plist. I'm able to get pywebview to open using this setting but unable to handle the file being opened. Here's the plist argument I'm passing in setup.py:

OPTIONS = {..
          'plist': {
                'CFBundleName': "MyApp",
                'CFBundleDisplayName': "MyApp",
                'CFBundleGetInfoString': "App it Up",
                'CFBundleIdentifier': "com.example.app",
                'CFBundleVersion': "0.1.0",
                "CFBundleTypeRole": "editor",
                "CFBundleTypeExtensions": ["appy-mc-apperson"],
                'CFBundleShortVersionString': "0.1.0",
                'NSHumanReadableCopyright': u"Copyright © 2020, Justin Mitchel, All Rights Reserved"
            }
}

The appy-mc-apperson represents something like my-project.appy-mc-apperson to, ideally, re-open our application and re-load the project. For context, .appy-mc-apperson is an arbitrary extension I made up for this post.

This works in Electron fairly simply as you can see here under fileAssociations. This would allow pywebview apps to resume projects that are being worked on which would be key for several projects I'm working on.

Practicalities

-YES. Happy to help however I can. -YES. I am prepared to support this issue financially within reason of course. I'm trying not to use Electron as much since it's so bloated especially for tiny projects.

r0x0r commented 4 years ago

I am not familiar with this API, but this definitely should be implemented. Without dwelling into details, I believe an appropriate event handler should be added to cocoa.py. Other platforms should be investigated as well.

github-actions[bot] commented 4 years ago

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

Ksengine commented 3 years ago

@jmitchel3 can you explain more

jmitchel3 commented 3 years ago

I wanted to add an update here based on something that's nearly working with py2app configuration.

OPTIONS = {
    "argv_emulation": True,
    ...
    "plist": {
        'CFBundleName': "MyApp",
                'CFBundleDisplayName': "MyApp",
                'CFBundleGetInfoString': "App it Up",
                'CFBundleIdentifier': "com.example.app",
                'CFBundleVersion': "0.1.0",
                "CFBundleTypeRole": "Editor",
                "CFBundleTypeExtensions": ["appy-mc-apperson"],
                "CFBundleTypeIconFile": "icon.icns",
                'CFBundleShortVersionString': "0.1.0",
                'NSHumanReadableCopyright': u"Copyright © 2020, Justin Mitchel, All Rights Reserved"
    },
}

Once you have "argv_emulation": True, the rest of your code will have access to another argument in sys.argv.

When you open your file extension, in this case I called it appy-mc-apperson which means if I double click my-project.appy-mc-apperson I see the following:

Screen Shot 2021-02-01 at 11 20 26 AM

Once you select your pywebview built app, my-project.appy-mc-apperson will always open your app with a new argument in sys.argv:

print(sys.argv[1]) 
# prints path/to/system/location/of/my-project.appy-mc-apperson

This sys.argv[1], if available, is a path to the my-project.appy-mc-apperson file. You can read this file as needed in your pywebview project.

If your app is not open, it works! (without changes to cocoa.py). However, f your app is open and you double-click to open an associated file you'll see this error:

Screen Shot 2021-02-01 at 11 24 33 AM.

I'd guess that pyinstaller might have a different way of implementing this same idea. It stands to reason that when you open any file on your system, the OS will open the application with an argument for that file path.

I think we still need to solve:

alvinwan commented 1 year ago

For anyone that stumbles upon this issue, you can solve the above issue by following these three steps:

  1. Switch to the webview.start(gui='qt') engine, as it's more configurable.
  2. Turn off argv_emulation. Rely on the qt engine's event handling instead of py2app's event handling.
  3. Add an event handler to webview's qt engine, similar to this SO answer.

Here's a minimal change to the hello world pywebview script that shows the idea.

app.py

import webview
import webview.platforms.qt
from qtpy.QtWidgets import QApplication
from qtpy.QtCore import QEvent
import webview.platforms.qt  # needed for us to define a custom application

class MainApplication(QApplication):
    def event(self, event: QEvent):
        if event.type() == QEvent.Type.FileOpen:
            print(event.url())
        return super().event(event)

if __name__ == '__main__':
    app = MainApplication([])  # so that when pywebview calls `QApplication.instance()` your app is returned
    webview.create_window('Hello world', 'https://pywebview.flowrl.com/')
    webview.start(gui='qt')

setup.py

"""Simple setup script for actually building your application. See README"""

from setuptools import setup

APP = ['app.py']  # your main application
OPTIONS = {
    'plist': {
        'CFBundleURLTypes': [
            {
                'CFBundleURLName': 'MyApplication',
                'CFBundleURLSchemes': ['myapp'],
            },
        ],
    },
    'strip': True,
    'includes': ['WebKit', 'Foundation', 'webview'],
}

setup(
    app=APP,
    options={'py2app': OPTIONS},
    setup_requires=['py2app', 'pyqt6'],  # add other dependencies here
)