将 URI 传递给捆绑的应用程序
Pass URI to Bundled Application
我有一个用 pyinstaller 创建的 .app。 info.plist
摘录如下:
...
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleURLName</key>
<string>Flagship</string>
<key>CFBundleURLSchemes</key>
<array>
<string>flagship</string>
</array>
</dict>
</array>
...
如果我在浏览器中打开任何以 flagship
开头的内容,即 flagship:foo
,然后它会启动应用程序。但是,sys.argv
仅包含 url 到已编译的 python 可执行文件。如何捕获 URI 调用的参数,即如何从 URI 获取 foo
?
更新 1:
因此,以下工作可以启动应用程序:
do shell script "open -a /Applications/AppName.app --args foo1 foo2"
当我使用捆绑为 .app 的 Apple 脚本并使用相同的 info.plist 选项时:
on open location this_URL
do shell script "open -a /Applications/AppName.app --args " & this_URL & ""
end open location
问题是:如何直接从 URI 实现类似的功能?如果可能的话,我想避免使用中介应用程序。
更新 2: 我找到了一个工作示例:https://pyobjc.readthedocs.io/en/latest/examples/WebKit/PyDocURLProtocol/index.html
看来是肯定可以的,就是蛋疼。如果我让它工作,将会更新。这个命令,比如pydoc:///credid=foo
在例子中完美的吐出来了,所以肯定是可以的
更新 3: 我最终使用 PySide6 进行了解决。我贴在下面了。
我最终使用 PySide6 解决了这个问题。使用 QApplication 的子类允许我定义一个事件处理程序来捕获 'file open' 事件......浏览器,即使它不是 'opening' 文件,仍然传递完整的 uri。代码如下:
from PySide6.QtCore import QEvent, QUrl
from PySide6.QtWidgets import QApplication
from ..logger import logger # NOTE: this code uses a packaged logger I built and you will need to replace it or remove all of the calls to logger to get this code to work.
class CustomURIApplication(QApplication):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.on_uri_do = None
self.last_uri = None
def on_uri_call(self, func):
"""
Run func when an uri is sent to the application; access the uri @ CustomURIApplication.last_uri
:param func: the function to run (WITHOUT "()" AT THE END); use lambda for parameters
:return: None
"""
self.on_uri_do = func
def event(self, e):
"""Handle macOS FileOpen events or pass to super."""
if e.type() == QEvent.FileOpen:
url: QUrl = e.url()
self.last_uri: QUrl = url
if url.isValid():
logger.info(f"application received valid uri: {url}")
logger.debug(f"executing callback function")
self.on_uri_do()
else:
logger.warning(f"application received invalid uri: {url.errorString()}")
else:
return super().event(e)
return True
Info.plist 需要包含我在原始问题中所拥有的内容,这仅在与 PyInstaller 捆绑后才有效。
我有一个用 pyinstaller 创建的 .app。 info.plist
摘录如下:
...
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleURLName</key>
<string>Flagship</string>
<key>CFBundleURLSchemes</key>
<array>
<string>flagship</string>
</array>
</dict>
</array>
...
如果我在浏览器中打开任何以 flagship
开头的内容,即 flagship:foo
,然后它会启动应用程序。但是,sys.argv
仅包含 url 到已编译的 python 可执行文件。如何捕获 URI 调用的参数,即如何从 URI 获取 foo
?
更新 1:
因此,以下工作可以启动应用程序:
do shell script "open -a /Applications/AppName.app --args foo1 foo2"
当我使用捆绑为 .app 的 Apple 脚本并使用相同的 info.plist 选项时:
on open location this_URL
do shell script "open -a /Applications/AppName.app --args " & this_URL & ""
end open location
问题是:如何直接从 URI 实现类似的功能?如果可能的话,我想避免使用中介应用程序。
更新 2: 我找到了一个工作示例:https://pyobjc.readthedocs.io/en/latest/examples/WebKit/PyDocURLProtocol/index.html
看来是肯定可以的,就是蛋疼。如果我让它工作,将会更新。这个命令,比如pydoc:///credid=foo
在例子中完美的吐出来了,所以肯定是可以的
更新 3: 我最终使用 PySide6 进行了解决。我贴在下面了。
我最终使用 PySide6 解决了这个问题。使用 QApplication 的子类允许我定义一个事件处理程序来捕获 'file open' 事件......浏览器,即使它不是 'opening' 文件,仍然传递完整的 uri。代码如下:
from PySide6.QtCore import QEvent, QUrl
from PySide6.QtWidgets import QApplication
from ..logger import logger # NOTE: this code uses a packaged logger I built and you will need to replace it or remove all of the calls to logger to get this code to work.
class CustomURIApplication(QApplication):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.on_uri_do = None
self.last_uri = None
def on_uri_call(self, func):
"""
Run func when an uri is sent to the application; access the uri @ CustomURIApplication.last_uri
:param func: the function to run (WITHOUT "()" AT THE END); use lambda for parameters
:return: None
"""
self.on_uri_do = func
def event(self, e):
"""Handle macOS FileOpen events or pass to super."""
if e.type() == QEvent.FileOpen:
url: QUrl = e.url()
self.last_uri: QUrl = url
if url.isValid():
logger.info(f"application received valid uri: {url}")
logger.debug(f"executing callback function")
self.on_uri_do()
else:
logger.warning(f"application received invalid uri: {url.errorString()}")
else:
return super().event(e)
return True
Info.plist 需要包含我在原始问题中所拥有的内容,这仅在与 PyInstaller 捆绑后才有效。