其实,在Windows、Mac、Linux等系统中实现的方法都是「注册应用程序协议」,比如说在Windows,只需在注册表中写入应用程序启动信息,在浏览器调用程序时,会先去注册表中查询应用信息,然后调用启动信息,即可实现协议与执行程序的关联。
Windows Registry Editor Version 5.00
[HKEY_CLASSES_ROOT\MYSCHEME]
"URL Protocol"="E:\\APP\\app.exe"
@="MYSCHEME.Protocol"
[HKEY_CLASSES_ROOT\MYSCHEME\DefaultIcon]
@="E:\\APP\\app.exe,1"
[HKEY_CLASSES_ROOT\MYSCHEME\shell]
[HKEY_CLASSES_ROOT\MYSCHEME\shell\open]
[HKEY_CLASSES_ROOT\MYSCHEME\shell\open\command]
@="\"E:\\APP\\app.exe\" --login-settings \"%1\""
上面的注册表信息,将告诉系统我们要注册一个自定义的URL Scheme,修改完成后,双击文件即可将信息写入注册表中。
此时我们可以写一个HTML文件,在文件中根据自己的情况修改 a标签:
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>浏览器启动本地应用title>
head>
<body>
<a href="MYSCHEME://?token=fjasfjasndfnas">打开你的应用程序a>
body>
html>
保存后,在浏览器打开此HTML文件,点击超链接,将会唤起 E:\APP\app.exe\ 并将参数–login-settings 「–login-settings=MYSCHEME://?token=fjasfjasndfnas」传递给应用程序。
此时,浏览器弹出弹窗提示用户是否启动本地应用程序,当用户选择启动后,则会将参数拼接至启动命令行中,启动程序。
E:\APP\app.exe --login-settings=“MYSCHEME://?token=fjasfjasndfnas”
macOS 下与 Windows 在自定义 URL 的实现上有差异,MAC中的方法简单一些,仅需要修改 Info.plist文件即可:
Info.plist 文件位置: 应用程序名称右键 —> 显示包内容 —> contents —> Info.plist 建议使用sublime打开文件
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleURLSchemes</key>
<array>
<string>MYSCHEME</string>
</array>
<key>CFBundleURLName</key>
<string>com.tencent.wemeet</string>
</dict>
</array>
其中 MYSCHEME 为自动注册到系统中的自定义 URL Scheme,当在 macOS 下通过浏览器访问 MYSCHEME:// 的地址时,浏览器将弹出弹窗提示用户是否启动本地应用程序。「注意:如果不提示的话,建议重新打包应用程序,重新生成info.plist文件」
注意:CFBundleURLName中的string为反DNS, 如果想知道其他字段设置:请移步官方文档
测试Mac的网页使用Windows的HTML即可,格式一致。
可在程序中使用 argparse.ArgumentParser() 方法设置参数
对传入的参数进行处理:
# 使用到urlparse模块对url进行解析
options, argv_rest = parser.parse_known_args(argv[1:])
if options.login_settings:
parsed_result = urlparse(options.login_settings).query
result = {k: v[0] for k, v in parse_qs(parsed_result).items()}
if result.get('token'):
print("设置Token:", result['token'])
参数设置的具体方法,可参考:https://blog.csdn.net/qq_42571592/article/details/122179398
MacOS下相对比较简单,其中 Qt 的方式非常简单,只需要响应应用的 QFileOpen 事件即可实现此功能,代码如下。
bool FileOpenEventFilter::eventFilter(QObject* obj, QEvent* event)
{
if (event->type() == QEvent::FileOpen)
{
QFileOpenEvent* fileEvent = static_cast<QFileOpenEvent*>(event);
if (!fileEvent->url().isEmpty())
{
m_lastUrl = fileEvent->url().toString();
emit urlOpened(m_lastUrl);
}
else if (!fileEvent->file().isEmpty())
{
emit fileOpened(fileEvent->file());
}
return false;
}
else
{
// standard event processing
return QObject::eventFilter(obj, event);
}
}
定义槽,具体槽的使用可看文章: https://blog.csdn.net/m0_37329910/article/details/86571800
1、声明带QUrl参数的信号
from AnyQt.QtCore import QUrl, pyqtSignal as Signal
self.fileOpenRequest = Signal(QUrl)
2、定义槽函数
def record_path(url: QUrl):
if url.toString().startswith("myscheme"):
parsed_result = urlparse(url.toString()).query
result = {k: v[0] for k, v in parse_qs(parsed_result).items()}
if result.get('token'):
print("设置Token:", result['token'])
else:
path = url.toLocalFile() # 本地文件路径
if os.path.exists(path): # 如果文件存在则打开
print("打开文件的路径为:", path)
3、将信号连接到指定槽函数
self.fileOpenRequest.connect(record_path)
4、发射信号
def event(self, event):
if event.type() == QEvent.FileOpen: # 对请求进行判断
self.fileOpenRequest.emit(event.url())
elif event.type() == QEvent.PolishRequest:
print("正常启动")
return super().event(event)
流程
使用浏览器启动应用时,QEvent进行监听,当event.type()为 QEvent.FileOpen时,对event进行处理。
原理:QEvent::FileOpen方法用于应用启动的两种方式:
所以,两种方式其实都是走的DNS格式,只需对这两种参数进行不同的处理,本地文件在应用中打开文件,浏览器的参数则进行处理。
Qt 通过自定义 URL Scheme 给已经运行的应用传参(Windows&macOS)
PyQt5官网
Mac开发者
PyQt 5信号与槽的几种高级玩法
QT论坛