浏览器调用GoldenDict查词

浏览器调用GoldenDict查词

GoldenDict不支持对Windows平台中Firefox浏览器的网页查词,下面提供一种利用Tampermonkey和Python配合实现查词的方案。

方案探索

在网络上可以查到执行GoldenDict.exe hello这条命令可以调出GoldenDict对hello这个词的查询小窗口,遂想到去看一下源代码中是怎么实现的。

在源代码的main.cc中的main函数有如下一行代码:

app.sendMessage( QString( "translateWord: " ) + gdcl.wordToTranslate() );

从这里我想到模拟这行代码是不是就可以调出查词小窗口。在网上搜寻一番后在https://github.com/qtproject/qt-solutions/blob/master/qtsingleapplication/src/qtlocalpeer.cpp找到了 sendMessage 的实现。

下面就是使用Python编写模仿调用的代码了,在代码中需注意函数connectToServer的参数,例:qtsingleapp-Golden-86bf-3,这个参数是在QtLocalPeer的构造函数中生成的,使用CRC-16/X25计算字符串GoldenDict得出86bf,后面的数字是登录Windows系统时的会话ID。

步骤

本方案仅供演示。

一、编写Tampermonkey用户脚本实现一个在网页中划词翻译的界面,选中词语后请求由Python脚本Web服务器提供的特定网址。

let id = 0
window.addEventListener('mouseup',(e)=>{
    let s = window.getSelection();
    if(s.toString().length > 0) {
        let elm = document.getElementById('goldendict')
        if (e.target === elm){
            return
        }
        elm.style.left = `${e.clientX}px`
        elm.style.top = `${e.clientY}px`
        elm.style.marginTop = window.getComputedStyle(s.anchorNode.parentElement)['font-size']
        elm.style.visibility = 'visible'
        elm.classList.toggle('dictshow',true)
        let lid = setTimeout(function(){
            if(id != lid) return
            elm.style.visibility = 'hidden'
        },3000)
        id = lid
    }
})
function initdict() {
    document.body.insertAdjacentHTML('beforeend',
        `<style>#goldendict.dictshow:hover{visibility:visible !important;}</style>
<button id="goldendict" class="dictshow" style="position:fixed;top:-100px;left:0px;width: max-content;background:white;" 
onclick="fetch('http://localhost:8080/query/'+window.getSelection().toString());this.classList.toggle('dictshow',false);this.style.visibility='hidden';"
>Search</button>`)
}
initdict()

效果如下:

1636125244003

二、编写Python脚本,接受浏览器请求,调用GoldenDict查词

要向GoldenDict传递信息需要安装PyQt5。最简单做法就是分析出要查的词后,直接调用os.system('GoldenDict.exe '+ word),这里使用Qt的LocalSocket。

代码由两部分组成:

  1. 简单的Web服务器
  2. 对照参考链接2中的QtLocalPeer::sendMessage函数编写的__opendict
import os
import re
from PyQt5.QtCore import QDataStream,QByteArray
from PyQt5.QtNetwork import QLocalSocket
import http.server
import socketserver
import ctypes

timeout = 3000
port = 8080

d = ctypes.c_long()
lockfile = 'qtsingleapp-Golden-86bf-'
if ctypes.windll.kernel32.ProcessIdToSessionId(os.getpid(),ctypes.byref(d)):
	lockfile = lockfile + str(d.value)
else:
	print("can't get the sessionid.")
	exit()

class HTTPRequestHandler(http.server.BaseHTTPRequestHandler):
	def __init__(self, request, client_address, server):
		
		self.lockfile = lockfile
		http.server.BaseHTTPRequestHandler.__init__(self, request, client_address, server)

	def do_GET(self):
		print(self.path)
		words = re.findall('^/query/([^/]+)$', self.path)

		if len(words) == 0:
			self.__send_code(404)
			return 

		self.__send_code(200)

		self.__opendict(words[0])

	def __send_code(self, code):
		self.send_response(code)
		self.send_header('Access-Control-Allow-Origin', '*')
		self.end_headers()

	def __opendict(self, word):

		if len(word) == 0:
			return
		try:
			soc = QLocalSocket()
			soc.connectToServer(self.lockfile)
			if not soc.waitForConnected(timeout):
				print(soc.errorString())
				return

			qdata = QDataStream(soc)

			msg = 'translateWord: ' + word
			qb = QByteArray(msg.encode())

			qdata.writeBytes(qb)
			s = soc.waitForBytesWritten(timeout)
			if s:
				s &= soc.waitForReadyRead(timeout)
				if s:
					print(soc.readAll().data().decode())

			print('LocalSocketError:%d'%(soc.error()))
			print('LocalSocketState:%d'%(soc.state()))

		except Exception as e:
			raise e
		finally:
			soc.disconnectFromServer()
			if soc.waitForConnected(1000):
				print("Socked Closed.")
			else:
				print('LocalSocketError on close:%d'%(soc.error()))


httpd = socketserver.TCPServer(('', port), HTTPRequestHandler)

try:
	httpd.serve_forever()
except KeyboardInterrupt:
	httpd.shutdown()
	exit()
	

在浏览器中访问http://localhost:8080/query/dictionary,GoldenDict会在你鼠标指针处弹出单词dictionary的查询小窗口。

参考链接:

https://github.com/goldendict/goldendict/blob/master/main.cc
https://github.com/qtproject/qt-solutions/blob/master/qtsingleapplication/src/qtlocalpeer.cpp
https://doc.qt.io/qt-5/qlocalsocket.html
https://stackoverflow.com/questions/12712360/qtsingleapplication-for-pyside-or-pyqt

posted @ 2021-11-06 00:16  还差得远呢~  阅读(321)  评论(0编辑  收藏  举报