记录 python 使用 tkinter 实现截屏翻译全过程

main.py

# _*_ coding: utf-8 _*_
"""
Time:     2021/7/11 15:47
Author:   WJY(YunYiJia)
Version:  V 0.1
File:     main.py
Describe: Blog link: https://blog.csdn.net/MeYungle
"""

from pynput.keyboard import Key, Listener

from view import ExampleApp


class Run:
    def __init__(self):
        self.press_ctrl = False
        self.press_alt = False
        self.running = False

    # 监听按下
    def on_press(self, key):
        if key == Key.ctrl_l:
            self.press_ctrl = True
        if key == Key.alt_l:
            self.press_alt = True
        if self.press_alt and str(key) == '<68>':
            if not self.running:  # 如果没有在运行中
                self.running = True
                print('启动程序')
                app = ExampleApp()
                app.mainloop()
                self.running = False

    # 监听抬起
    def on_release(self, key):
        if key == Key.ctrl_l:
            self.press_ctrl = False
        if key == Key.alt_l:
            self.press_ctrl = False

    def listener_key(self):
        print('监听热键Ctrl + Alt + D')
        with Listener(on_press=self.on_press, on_release=self.on_release) as listener:
            listener.join()
            # 停止监视
            listener.stop()


if __name__ == '__main__':
    run = Run()
    run.listener_key()

baiDuAI.py

# _*_ coding: utf-8 _*_
"""
Time:     2021/7/11 15:21
Author:   WJY(YunYiJia)
Version:  V 0.1
File:     baiDuAI.py
Describe: Blog link: https://blog.csdn.net/MeYungle
"""

# encoding:utf-8

import random
import time

import requests
from hashlib import md5


# 百度Ai开放平台
# client_id 为官网获取的AK, client_secret 为官网获取的SK
def get_token():
    host = 'https://aip.baidubce.com/oauth/2.0/token'
    params = {
        "grant_type": "client_credentials",
        "client_id": "wZn5KOfHTystucXXXXXXXX",
        "client_secret": "7uxYWK5KdyBXXXXXXXXXXX"

    }
    response = requests.get(host, params=params)
    if response:
        return response.json()['access_token']


def ocr_api(img_io):
    import base64
    request_url = "https://aip.baidubce.com/rest/2.0/ocr/v1/general"
    # 二进制方式打开图片文件
    # f = open('txtImg.jpg', 'rb')
    img = base64.b64encode(img_io)
    headers = {'content-type': 'application/x-www-form-urlencoded'}
    response = requests.post(request_url, params={'access_token': get_token()}, data={"image": img}, headers=headers)
    if response:
        print(response.json())
        return response.json().get('words_result', [])


def fanyi(word):
    # return fanyi_by_baidu(word)
    return fanyi_by_youdao(word)


# 百度翻译开放平台
# 有查询限制,免费接口每秒只能查询一次
def fanyi_by_baidu(word):
    params = {
        'q': str(word),
        'from': 'en',
        'to': 'zh',
        'appid': '20210711000800000',
        'salt': str(int(time.time()))
    }
    sign_str = params['appid'] + params['q'] + params['salt'] + 'aPw7Lixxxxxxxxxx'
    sign = md5(sign_str.encode()).hexdigest()
    url = 'https://fanyi-api.baidu.com/api/trans/vip/translate'
    res = requests.get(url, params={**params, 'sign': sign})
    print(res.json())
    return res.json()['trans_result'][0]['dst']


# 无限制
def fanyi_by_youdao(word, lang=None):
    if lang is None:
        lang = ['AUTO', 'AUTO']
    headers = {
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.163 Safari/537.36',
        'Referer': 'http://fanyi.youdao.com/',
        'Cookie': 'OUTFOX_SEARCH_USER_ID=-148473857@10.169.0.83; JSESSIONID=aaafbpaaZm1Q5-_wxwLgx; OUTFOX_SEARCH_USER_ID_NCOO=982336911.512214; ___rl__test__cookies=1587617780880',
    }

    data = {
        'i': word, 'from': lang[0], 'to': lang[1], 'smartresult': 'dict', 'client': 'fanyideskweb',
        'salt': None, 'sign': None, 'ts': None,
        'bv': md5(headers['User-Agent'].encode('utf-8')).hexdigest(),
        'doctype': 'json', 'version': '2.1', 'keyfrom': 'fanyi.web',
        'action': 'FY_BY_CLICKBUTTION'
    }
    ts = str(int(time.time()) * 1000)
    salt = ts + str(int(random.random() * 10))
    data['i'] = word
    data['ts'] = ts
    data['salt'] = salt
    data['sign'] = md5(f"fanyideskweb{word}{salt}Y2FYu%TNSbMCxc3t2u^XT".encode('utf-8')).hexdigest()

    #  发送请求  提取数据
    url = 'http://fanyi.youdao.com/translate_o?smartresult=dict&smartresult=rule'
    response = requests.post(url, data=data, headers=headers)
    print(word)
    print(response.text)
    res = response.json().get('translateResult')
    # print(res)
    # 如果无法识别语种则使用 英译中 进行翻译
    return ''.join([res['tgt'] for res in res[0]]) if res else fanyi_by_youdao(word, ['en', 'zh-CHS'])


if __name__ == '__main__':
    print(fanyi('hello'))

view.py

# _*_ coding: utf-8 _*_
"""
Time:     2021/7/11 12:19
Author:   WJY(YunYiJia)
Version:  V 0.1
File:     view.py
Describe: Blog link: https://blog.csdn.net/MeYungle
"""
import ctypes
import time
import tkinter as tk
from PIL import Image, ImageTk, ImageFilter, ImageGrab
from io import BytesIO

from baiDuAI import ocr_api, fanyi

# 调用api设置成由应用程序缩放(防止模糊)
ctypes.windll.shcore.SetProcessDpiAwareness(1)


# 调用api获得当前的缩放因子(当前缩放比例)
# ScaleFactor = ctypes.windll.shcore.GetScaleFactorForDevice(0)


def xy_change(x_begin, y_begin, x_end, y_end):
    if x_begin < x_end:
        min_x = x_begin
        max_x = x_end
    else:
        min_x = x_end
        max_x = x_begin
    if y_begin < y_end:
        min_y = y_begin
        max_y = y_end
    else:
        min_y = y_end
        max_y = y_begin

    return (min_x, min_y), (max_x, max_y)


def img_as_io(img):
    buffer = BytesIO()  # 创建二进制数据IO缓存
    img.save(buffer, 'PNG')  # 保存到缓存
    return buffer.getvalue()  # 再读取缓存


def txt_colour_compute(img):
    """
    :param img: PIL图片对象
    :return: 根据图片的四个角的颜色值,获取图片颜色的反色
    """
    img_value = img.load()  # 加载图片颜色值分布
    x, y = img.size  # 截取的区域图片大小
    average_value = sum(img_value[1, 1] + img_value[x - 1, 1] + img_value[1, y - 1] + img_value[x - 1, y - 1])
    return 'white' if average_value < 1530 else 'black'


class ExampleApp(tk.Tk):
    def __init__(self):
        tk.Tk.__init__(self)
        # 设置缩放因子
        # self.tk.call('tk', 'scaling', ScaleFactor / 75)
        self.width = self.winfo_screenwidth()  # 获取当前屏幕宽度 缩放后的(不符合需求后期要改)
        self.height = self.winfo_screenheight()  # 获取当前屏幕高度

        self.attributes("-fullscreen", True)  # 铺满全屏
        self.x = self.y = 0
        # 创建画布,设置无边框
        self.canvas = tk.Canvas(self, width=self.width, height=self.height, cursor="cross", bd=0, highlightthickness=0)
        self.canvas.pack(side="top", fill="both", expand=True)
        self.canvas.bind("<ButtonPress-1>", self.on_button_press)  # 绑定鼠标左键按下
        self.canvas.bind("<B1-Motion>", self.on_move_press)  # 绑定鼠标移动
        self.canvas.bind("<ButtonRelease-1>", self.on_button_release)  # 绑定鼠标左键抬起
        self.canvas.bind("<ButtonRelease-3>", lambda x: self.destroy())  # 绑定右键 退出主事件循环

        self.rect = None

        self.start_x = None
        self.start_y = None

        self.region_bg = None
        self.txt_colour = None
        self.translate_result = []

        self._draw_image()

    def _draw_image(self):
        # self.im = Image.open('01.png')
        self.im = ImageGrab.grab()  # 截取屏幕
        self.im = self.im.resize((self.width, self.height), Image.LANCZOS)  # 缩放图片
        self.tk_im = ImageTk.PhotoImage(self.im)  # 图片对象转换
        self.canvas.create_image(0, 0, anchor="nw", image=self.tk_im)  # 创建画布图片

    def on_button_press(self, event):
        # 保存鼠标拖动开始位置
        self.start_x = event.x
        self.start_y = event.y

        for res in self.translate_result:
            self.canvas.delete(res)
        self.translate_result.clear()

        # 如果不存在则创建矩形
        if not self.rect:
            # 创建矩形,设置矩形线条颜色
            self.rect = self.canvas.create_rectangle(self.x, self.y, 1, 1, outline='Orange')

    def on_move_press(self, event):
        cur_x, cur_y = (event.x, event.y)

        # 拖动鼠标时展开矩形
        self.canvas.coords(self.rect, self.start_x, self.start_y, cur_x, cur_y)

    def on_button_release(self, event):
        start_xy, end_xy = xy_change(self.start_x, self.start_y, event.x, event.y)  # 坐标处理处理反向截取矩形
        self.start_x, self.start_y = start_xy
        endx, endy = end_xy
        region = self.im.crop((*start_xy, endx + 1, endy + 1))  # 截取矩形内的图像(加大1px防止有边线)
        if sum(region.size) > 10:  # 判断是否位误触截取区域小于10px不做操作
            self.translate(region)

    def translate(self, region):
        self.txt_colour = txt_colour_compute(region)  # 粗略计算文本颜色
        self.region_bg = ImageTk.PhotoImage(region.filter(ImageFilter.GaussianBlur(radius=3)))  # 高斯模糊后当作背景
        bg = self.canvas.create_image(self.start_x, self.start_y, anchor="nw", image=self.region_bg)  # 添加文字背景
        self.translate_result.append(bg)  # 把文字背景添加到本次翻译结果列表,方便一次性删除
        msg_inx = self.canvas.create_text(  # 添加提示文字
            *map(lambda a, b: a + b, (self.start_x, self.start_y), (60, 12)), text='正在翻译请稍后', font=("微软雅黑", 12),
            fill=self.txt_colour
        )
        words = ocr_api(img_as_io(region))  # img对象转BytesIO后 调用Baidu文字识别api
        for word in words:
            self.add_txt(
                fanyi(word['words']),
                word['location']['left'],
                word['location']['top'],
                word['location']['width'],
                word['location']['height']
            )
            # time.sleep(1)  # 因百度免费api每秒查询量为1所以这里睡眠1秒

        self.canvas.delete(msg_inx)  # 删除提示文字

    def add_txt(self, word, word_x, word_y, word_w, word_h):
        """
        :param word: 文字
        :param word_x: 文字x坐标(在截取图片中的坐标/相对坐标)
        :param word_y: 文字y坐标(相对相对坐标)
        :param word_w: 文字宽度
        :param word_h: 文字高度
        :return:
        """
        start_xy = self.start_x, self.start_y  # 起始坐标
        word_xy = word_x, word_y  # 文字坐标
        word_width_height = int(word_w // 2), int(word_h // 2)  # 文字宽高
        location = map(lambda a, b, c: a + b + c, start_xy, word_xy, word_width_height)  # 定位文字位置
        # 下面这行代码用于控制文字位置、样式、大小和颜色
        inx = self.canvas.create_text(*location, text=word, font=("微软雅黑", int(word_h // 2)), fill=self.txt_colour)
        self.translate_result.append(inx)


if __name__ == "__main__":
    app = ExampleApp()
    app.mainloop()

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

CtrlCV工程师

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值