python:tkinterweb 简单又好用的 htmlview 组件

tkinterweb  是简单又好用的 webview 组件,也可用来显示本地 html文件。

pip install tkinterweb ;

pip install readmdict ; 参见: 使用Python调用mdx字典文件进行查词

安装 MDict 去 MDict | One app for all dictionaries 下载

测试 html view 程序 tk_read_mdict.py 如下:

# -*- coding: utf-8 -*-
""" 查询英汉词典 """
import os
import time
import tkinter as tk
from tkinter import filedialog
from tkinter import messagebox
from tkinterweb import HtmlFrame
import pygame
from readmdict import MDX  # pip install readmdict
import win32com.client  # TTS
sapi = win32com.client.Dispatch("SAPI.SpVoice")

#pygame.init()
pygame.mixer.init()

os.chdir("/MDictPC/doc")

def speak():
    """ TTS发音 """
    txt = frame.get_currently_selected_text()
    if txt == None:
        txt = entry.get()
    if txt.strip() !='':
        sapi.Speak(txt)

def mplay(filename):
    """播放.mp3 """
    if pygame.mixer.music.get_busy():
        print("mixer.music.get_busy")
        return 1
    if not os.path.exists(filename):
        print(f"{filename} not found.")
        return 2
    fn,ext = os.path.splitext(filename)
    if ext.lower() == '.mp3':
        pygame.mixer.music.load(filename)
        pygame.mixer.music.play()
    else:
        print(f"{filename} is not .mp3")
        return 3
    return 0


def open_mdx():
    """ 读.mdx词典文件 """
    file1 = ''
    filetypes = [('mdx file','.mdx')]
    file1 = filedialog.askopenfilename(initialdir=".", filetypes=filetypes)
    if file1 == '': return
    start_time = time.time()
    path1 = os.path.dirname(file1)
    os.chdir(path1)
    global headwords, items
    # 加载.mdx文件
    fname = os.path.basename(file1)
    mdx = MDX(fname)
    headwords = [*mdx]       # 单词名列表
    items = [*mdx.items()]   # 释义html源码列表
    n = len(headwords)
    m = len(items)
    end_time = time.time()
    print('cost %f second' % (end_time - start_time))
    if n == m:
        var1.set(f'{fname} 加载成功:共{n}条')
    else:
        var1.set(f'ERROR:{fname} 加载失败 {n}!={m}')
        return

def query(self):
    """  输入英文单词,并查询 """
    if not entry.get():
        messagebox.showinfo("请先选择词典","输入单词不能为空")
        return
    txt = entry.get()
    if txt.startswith("http://") or txt.startswith("https://"):
        frame.load_url(txt)
        frame.on_link_click(sound)
    else:
        prefix(txt)
        query1(txt)

def query1(txt):
    """  查询英文单词 """
    global var1, headwords
    try:
        type(headwords)
    except NameError:
        messagebox.showinfo("提示", "请先选择词典!")
        return
    word = txt.lower().encode()
    word1 = txt.capitalize().encode() # 第1个字母变大写
    try: # 查词,返回单词和html文件
        if word in headwords:
            wordIndex = headwords.index(word)
        else:
            wordIndex = headwords.index(word1)
        word,html = items[wordIndex]
        result = html.decode()
        if result.startswith('@@@LINK='):
            w = result[8:].strip()
            result = '<a href="entry://' +w+ '">' +w+ '</a>'
        else:
            result = result.replace('<img src="','<img src="./data/')
            result = result.replace('"/thumb/','"data/thumb/')
        frame.enable_stylesheets(enabled=True)
        frame.enable_images(enabled=True)
        frame.load_html(result)
        frame.on_link_click(sound)
    except:
        var1.set(f"{txt} is not in word_list.")


def prefix(txt):
    """ 前缀匹配 """
    global var1, headwords
    try:
        type(headwords)
    except NameError:
        print('headwords is undefined.')
        return
    if len(txt) > 2:
        listbox.delete(0, tk.END)
        word = txt.strip().lower() # 字母变小写
        for hw in headwords:
            hws = hw.decode().lower()
            if hws.startswith(word):
                listbox.insert(tk.END, hw.decode())
    else:
        var1.set(f"{txt} input too short")    

def sound(url):
    """ 处理<a>链接点击事件 """
    print(url)
    if url.startswith("http://"):
        frame.load_url(url)
        frame.on_link_click(sound)
    elif url.startswith("entry://"):
        query1(url[8:])
    elif url.startswith("sound://"):
        mp3file = 'data' + url[7:]
        mplay(mp3file)
    elif url.startswith('#'):
        frame.skim(url)  #anchor
    else:
        pass

def selected(self):
    """ 选择列表框 """
    try:
        index = listbox.curselection()
        txt = listbox.get(index,last=None)
        sapi.Speak(txt)
        query1(txt)
    except:
        print('listbox index is null.')

def search():
    """ 访问http://localhost:???? """
    url = entry.get()
    if url.startswith('http://'):
        frame.load_url(url)
        frame.on_link_click(sound)
        return

# main()
root = tk.Tk()
root.title("查询英汉词典")
root.geometry("1000x500")
root.resizable(width=True, height=False)
# 输入框布局
frame1 = tk.Frame(root, width=80, height=30)
frame1.pack(side="top")
entry = tk.Entry(frame1, width=50)
entry.pack(side="left", padx=5, pady=2)
entry.bind("<Return>", query) # 按下回车,执行查单词
button1 = tk.Button(frame1, text="选择词典", command=open_mdx)
button1.pack(side="left", padx=5, pady=2)
button2 = tk.Button(frame1, text="TTS读单词", command=speak)
button2.pack(side="left", padx=5, pady=2)
button3 = tk.Button(frame1, text="前缀查找", command=search)
button3.pack(side="left", padx=5, pady=2)
# 退出按钮
quit_btn = tk.Button(frame1, text="退出", fg="red", command=root.quit)
quit_btn.pack(side="right", padx=5, pady=2)
# 显示栏
var1 = tk.StringVar()
label = tk.Label(root,textvariable=var1,fg="purple", font="Song 12")
var1.set("请先选择词典")
label.pack()
# 列表框
listbox = tk.Listbox(root, width=25, font="Song 12")
listbox.bind('<<ListboxSelect>>', selected)
listbox.pack(side="left", fill='both')
#create HTML browser
frame = HtmlFrame(root, messages_enabled=False) # False:关闭debug消息
frame.pack(side="right", fill="both", expand=True, padx=5, pady=2)

root.mainloop()

可以先解压.mdd文件:python -m readmdict -x  your.mdd

运行 python tk_read_mdict.py

from tkinterweb import HtmlFrame
help(HtmlFrame)

 |  load_file(self, file_url, decode=None, force=False)
 |      Load a locally stored file from the specified path
 |
 |  load_form_data(self, url, data, method='GET', decode=None)
 |      Load a webpage using form data
 |
 |  load_html(self, html_source, base_url=None)
 |      Reset parser and send html code to it
 |
 |  load_url(self, url, decode=None, force=False)
 |      Load a website (https:// or http://) or a file (file://) from the specified URL.
 |      We use threading here to prevent the GUI from freezing while fetching the website.

关注这一句:  frame.on_link_click(sound)

def sound(url):
    """ 处理<a>链接点击事件 """
    print(url)
    if url.startswith("http://"):
        frame.load_url(url)
        frame.on_link_click(sound)
    elif url.startswith("entry://"):
        query1(url[8:])
    elif url.startswith("sound://"):
        mp3file = 'data' + url[7:]
        mplay(mp3file)
    elif url.startswith('#'):
        frame.skim(url)  #anchor
    else:
        pass


tkinterWeb offers bindings for the Tkhtml3 widget, 可惜 Tkhtml3 不支持 javascript.

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值