Closed czqhurricnae closed 1 year ago
因为子线程调用qt图形代码了,应该用@postGui 包装一下函数
我尝试用 @postGui 装饰器包装了,pop_anki_review_window,expanded_on_bridge_cmd。都没有效果。
def pop_anki_review_window(popweb, module_name, index_file, x, y,
x_offset, y_offset, frame_x, frame_y, frame_w, frame_h,
width_scale, height_scale, show_window,
script_file, media_directory,
new_query_p, emacs_query):
web_window = popweb.get_web_window(module_name)
window_width = frame_w * width_scale
window_height = frame_h * height_scale
window_x, window_y = popweb.adjust_render_pos(x + x_offset, y + y_offset,
x_offset, y_offset,
window_width, window_height,
frame_x, frame_y,
frame_w, frame_h)
class CallHandler(QObject):
@pyqtSlot(QVariant, result=QVariant)
def expanded_on_bridge_cmd(self, cmd: str) -> Tuple[bool, Any]:
if cmd.startswith("rev-search "):
tooltip_id = int(cmd.split()[1])
query = " ".join(cmd.split()[2:])
if (query.isascii() and len(query) >= 3) or (not query.isascii() and len(query) >= 2):
notes = get_notes(query)
web_window.web_page.runJavaScript(f"setTooltipSearchResults({tooltip_id}, {json.dumps(notes)})")
return (True, None)
elif cmd.startswith("rev-tt-edit "):
invoke("guiEditNote", note=int(cmd.split()[1]))
return (True, None)
elif cmd.startswith("rev-tt-playsound "):
tooltip_id = int(cmd.split()[1])
note = invoke("notesInfo", notes=[tooltip_id])
results = re.findall('\[sound:(.+?\..+?)\]', str(note))
if not results:
return (True, None)
if len(results) == 1:
playsound(os.path.expanduser(os.path.join(media_directory, results[0])))
return (True, None)
for result in results:
playsound(os.path.expanduser(os.path.join(media_directory, result)))
return (True, None)
web_window.webview.channel = QWebChannel()
web_window.webview.handler = CallHandler()
web_window.webview.channel.registerObject('handler', web_window.webview.handler)
web_window.webview.page().setWebChannel(web_window.webview.channel)
index_html = open(index_file, "r").read().replace(
"QUERY_PLACEHOLD", emacs_query).replace(
"HEAD-PLACEHOLD", get_tooltip_script(script_file))
web_window.loading_js_code = ""
web_window.webview.setHtml(index_html, QUrl(index_file))
web_window.web_page.loadFinished.connect(lambda: web_window.web_page.runJavaScript(f"tooltips()"))
也尝试过在 popweb.py 文件中的 hide_web_window
中尝试增加:
web_window.webview.handler.deleteLater()
web_window.webview.channel.deleteLater()
也没有效果。
我对 pyqt 没有接触过,完全是在盲人摸象,找了一下午,都没有头绪。
我要周末看了, 这几天超级忙。
新的提交已经解决该问题。
怎么解决的呀
刚开始,听取你的建议,要使用 @PostGui() 。
在 popweb-anki-review.py:
def pop_anki_review_window(popweb, module_name, index_file, x, y,
x_offset, y_offset, frame_x, frame_y, frame_w, frame_h,
width_scale, height_scale, show_window,
script_file, media_directory,
new_query_p, emacs_query):
web_window = popweb.get_web_window(module_name)
window_width = frame_w * width_scale
window_height = frame_h * height_scale
window_x, window_y = popweb.adjust_render_pos(x + x_offset, y + y_offset,
x_offset, y_offset,
window_width, window_height,
frame_x, frame_y,
frame_w, frame_h)
class CallHandler(QObject):
@pyqtSlot(QVariant, result=QVariant)
def expanded_on_bridge_cmd(self, cmd: str) -> Tuple[bool, Any]:
if cmd.startswith("rev-search "):
tooltip_id = int(cmd.split()[1])
query = " ".join(cmd.split()[2:])
if (query.isascii() and len(query) >= 3) or (not query.isascii() and len(query) >= 2):
notes = get_notes(query)
web_window.web_page.runJavaScript(f"setTooltipSearchResults({tooltip_id}, {json.dumps(notes)})")
return (True, None)
elif cmd.startswith("rev-tt-edit "):
invoke("guiEditNote", note=int(cmd.split()[1]))
return (True, None)
elif cmd.startswith("rev-tt-playsound "):
tooltip_id = int(cmd.split()[1])
note = invoke("notesInfo", notes=[tooltip_id])
results = re.findall('\[sound:(.+?\..+?)\]', str(note))
if not results:
return (True, None)
if len(results) == 1:
playsound(os.path.expanduser(os.path.join(media_directory, results[0])))
return (True, None)
for result in results:
playsound(os.path.expanduser(os.path.join(media_directory, result)))
return (True, None)
web_window.webview.channel = QWebChannel()
web_window.webview.handler = CallHandler()
web_window.webview.channel.registerObject('handler', web_window.webview.handler)
web_window.webview.page().setWebChannel(web_window.webview.channel)
index_html = open(index_file, "r").read().replace(
"QUERY_PLACEHOLD", emacs_query).replace(
"HEAD-PLACEHOLD", get_tooltip_script(script_file))
web_window.loading_js_code = ""
web_window.webview.setHtml(index_html, QUrl(index_file))
web_window.web_page.loadFinished.connect(lambda: web_window.web_page.runJavaScript(f"tooltips()"))
尝试过: 1.
@PostGui()
def pop_anki_review_window
2.
@PostGui()
@pyqtSlot(QVariant, result=QVariant)
def expanded_on_bridge_cmd
3.
@pyqtSlot(QVariant, result=QVariant)
@PostGui()
def expanded_on_bridge_cmd
4.
PostGui()(web_window.webview.channel.registerObject('handler', web_window.webview.handler))
PostGui()(web_window.webview.page().setWebChannel(web_window.webview.channel))
这几种方法后没有解决。
然后尝试将 Python 与 JS 通信的代码都写到 popweb.py 中,发现没有再出现该错误。
我研究了一下 PostGui 装饰器的代码,去找了 PyQt 的教程阅读了,了解一些 信号和槽。
我猜测 Python 与 JS 通信的注册代码:
web_window.webview.channel = QWebChannel()
web_window.webview.handler = CallHandler()
web_window.webview.channel.registerObject('handler', web_window.webview.handler)
web_window.webview.page().setWebChannel(web_window.webview.channel)
写在主线程 popweb.py 中不会产生错误。
但是因为 class CallHandler()
中很多的逻辑代码只想放在 popweb-anki-review.py 中。
所以就使用:
@PostGui()
def build_web_channel(self, module_path, module_name, slot_class_name, slot_method_name, slot_method_args, expose_to_js_handler_name):
module = self.get_module(module_path)
slot_class = getattr(module, slot_class_name)
slot_method = getattr(slot_class, slot_method_name)
slot_class.slot_method_name = QtCore.pyqtSlot(QtCore.QVariant, result=QtCore.QVariant)(slot_method)
web_window = self.get_web_window(module_name)
web_window.webview.channel = QWebChannel()
web_window.webview.handler = slot_class(web_window, slot_method_args)
web_window.webview.channel.registerObject(expose_to_js_handler_name, web_window.webview.handler)
web_window.webview.page().setWebChannel(web_window.webview.channel)
利用 PostGui 中信号与槽的技术,把逻辑代码 class CallHandler()
与
web_window.webview.channel = QWebChannel()
web_window.webview.handler = slot_class(web_window, slot_method_args)
web_window.webview.channel.registerObject(expose_to_js_handler_name, web_window.webview.handler)
web_window.webview.page().setWebChannel(web_window.webview.channel)
这段 Python 与 JS 通信的注册代码分离。
总之:Python 与 JS 通信的注册代码在主线程运行是关键。
不知道我的理解对不对?还是只是误打误撞?
补丁的截图能否换一下其他图片或者不显示图片?
这张图片本身会显示到README中, 有可能会因为图片本身让用户误解本项目。
大佬,换一下gif吧
我把 gif 删除了。
刚发现,popweb-anki-review-show 命令在运行成功一次后,在 popweb 窗口 hide 后,总是出现:
在运行 popweb-anki-review-show 后 popweb 窗口无法 popup。需要重启 popweb。
经过排查,只有将:
https://github.com/czqhurricnae/popweb/blob/47133d309c50a3860966f40a8b02b1ae6a851f2d/extension/anki-review/popweb-anki-review.py#L277-L278
注释掉,该错误才不会出现。
但是这样就无法使得 popweb 的 Python 与 JS 进行通信。
是不是应该在 popweb 窗口 hide 同时对通信用的
channel
进行处理?但是怎么处理我不懂,google 也没能找到。