Skip to content

Commit

Permalink
新功能:消息弹窗,弹出任务完成通知(#86
Browse files Browse the repository at this point in the history
  • Loading branch information
hiroi-sora committed Mar 4, 2023
1 parent b3a8737 commit 7c37fc7
Show file tree
Hide file tree
Showing 5 changed files with 207 additions and 5 deletions.
7 changes: 7 additions & 0 deletions ocr/msn_batch_paths.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# 批量路径 任务处理器

from utils.config import Config
from ui.win_notify import Notify # 通知弹窗
from ocr.engine import MsnFlag
from ocr.msn import Msn
# 输出器
Expand Down Expand Up @@ -165,6 +166,12 @@ def onStop(self, num):
l = len(self.outputList)
for i in range(1, l):
self.outputList[i].openOutputFile()
if Config.get('isNotify'): # 通知弹窗
title = f'识别完成,共{num["all"]}张图片'
msg = '结果未保存到本地文件,请在软件面板查看'
if Config.get('isOutputTxt') or Config.get('isOutputSeparateTxt') or Config.get('isOutputMD') or Config.get('isOutputJsonl'):
msg = f'结果保存到:{Config.get("outputFilePath")}'
Notify(title, msg)
if Config.get("isOkMission"): # 计划任务
Config.set("isOkMission", False) # 一次性,设回false
omName = Config.get('okMissionName')
Expand Down
6 changes: 6 additions & 0 deletions ocr/msn_quick.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from ocr.engine import MsnFlag
from ocr.msn import Msn
from ocr.output_panel import OutputPanel # 输出器
from ui.win_notify import Notify # 通知弹窗

import tkinter as tk
import time
Expand Down Expand Up @@ -54,10 +55,12 @@ def onGet(self, numData, ocrData):
tbLen = len(tbList)
if tbLen == 0:
self.outputPanel.print('不存在有效文字\n')
Notify('未发现文字', '')
return
tbTexts = [tb['text'] for tb in tbList] # 提取文字
tbStr = '\n'.join(tbTexts)
self.outputPanel.print(tbStr) # 输出到面板
Notify('识别完成', tbStr)
if Config.get('isNeedCopy'): # 需要复制
pyperclipCopy(tbStr) # 复制到剪贴板
# 计算置信度
Expand All @@ -67,10 +70,13 @@ def onGet(self, numData, ocrData):
elif ocrData['code'] == 101: # 无文字
Config.set('tipsTop1', f'耗时:{round(numData["time"], 2)}s 无文字')
self.outputPanel.print('未发现文字\n')
Notify('未发现文字', '')
else: # 识别失败
Config.set('tipsTop1', f'耗时:{round(numData["time"], 2)}s 识别失败')
self.outputPanel.print(
f'识别失败,错误码:{ocrData["code"]}\n错误信息:{str(ocrData["data"])}\n')
Notify(
'识别失败', f'错误码:{ocrData["code"]}\n错误信息:{str(ocrData["data"])}\n')

def onStop(self, num):
Config.main.setRunning(MsnFlag.none)
25 changes: 20 additions & 5 deletions ui/win_main.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from utils.startup import Startup # 启动方式
from utils.hotkey import Hotkey # 快捷键
from utils.command_arg import Parse, Mission # 启动参数分析
from ui.win_notify import Notify # 通知弹窗
from ui.win_screenshot import ScreenshotCopy # 截屏
from ui.win_select_area import IgnoreAreaWin # 子窗口
from ui.win_ocr_language import ChangeOcrLanguage # 更改语言
Expand Down Expand Up @@ -382,16 +383,29 @@ def updateTextpanel():
self.balloon.bind(
wid, '不会主动弹出窗口')

# 启动方式设置
# 消息弹窗设置

def changeNotify():
if Config.get('isNotify'):
Notify('欢迎使用 Umi-OCR', '通知弹窗已开启')
fr4 = tk.Frame(fSoft)
fr4.pack(side='top', fill='x', pady=2, padx=5)
self.balloon.bind(
fr4, '可设置静默启动,收纳到系统托盘,不显示主窗口')
ttk.Checkbutton(fr4, variable=Config.getTK('isAutoStartup'),
fr4, '识图完成后弹出通知\n建议与窗口置顶的“不要弹出”模式搭配使用')
ttk.Checkbutton(
fr4, variable=Config.getTK('isNotify'), text='启用通知弹窗').pack(side='left')
Config.addTrace('isNotify', changeNotify)

# 启动方式设置
fr5 = tk.Frame(fSoft)
fr5.pack(side='top', fill='x', pady=2, padx=5)
self.balloon.bind(
fr5, '可设置静默启动,收纳到系统托盘,不显示主窗口')
ttk.Checkbutton(fr5, variable=Config.getTK('isAutoStartup'),
text='开机自启', command=Startup.switchAutoStartup).pack(side='left')
ttk.Checkbutton(fr4, variable=Config.getTK('isStartMenu'),
ttk.Checkbutton(fr5, variable=Config.getTK('isStartMenu'),
text='开始菜单项', command=Startup.switchStartMenu).pack(side='left', padx=20)
ttk.Checkbutton(fr4, variable=Config.getTK('isDesktop'),
ttk.Checkbutton(fr5, variable=Config.getTK('isDesktop'),
text='桌面快捷方式', command=Startup.switchDesktop).pack(side='left')
initSoftwareFrame()

Expand Down Expand Up @@ -889,6 +903,7 @@ def onCanvasMouseWheel(event): # 绑定画布中滚轮滚动事件
self.win.after(1, Config.initOK) # 标记初始化完成
if flags['img'] or flags['clipboard'] or flags['screenshot']: # 有初始任务
self.win.after(10, Mission(flags))
Notify('欢迎使用 Umi-OCR', '通知弹窗已开启')
self.win.mainloop()

# 加载图片 ===============================================
Expand Down
169 changes: 169 additions & 0 deletions ui/win_notify.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
from utils.config import Config

import tkinter as tk
from enum import Enum

winW, winH = 300, 60 # 宽高
winR = 15 # 圆角半径
winPosY = 60 # 窗口与屏幕下方的距离
xxS1 = 14 # 叉叉进度条大小(直径)
xxS2 = 10 # 叉叉进度条离右上角的距离
bgColor = '#FFCBE0' # 背景色
teS1 = 12 # 主标题字号
teS2 = 9 # 次内容字号
teP1 = (12, 9) # 主标题位置
teP2 = (12, 35) # 次内容位置
popTime = 0.4 # 弹出动画时长,秒
downTime = 6 # 自动关闭倒计时时长,秒
frameTime = 30 # 动画帧间隔,毫秒
winR2 = winR*2


class State(Enum):
none = 0 # 无
starting = 1 # 弹出中
showing = 2 # 显示中


class NotifyWindow():
def __init__(self):
self.win = None
self.state = State.none
self.afters = [None, None, None] # 保存运行中的计时器

def __initWin(self): # 初始化窗体
self.win = tk.Toplevel()
self.win.withdraw()
self.win.attributes("-alpha", 0) # 透明度

winX = int((self.win.winfo_screenwidth() - winW) / 2) # 位置:屏幕中下
winY = int(self.win.winfo_screenheight() - winH - winPosY)
self.win.geometry(f'{winW}x{winH}+{winX}+{winY}')
self.win.resizable(False, False) # 锁定大小
self.win.overrideredirect(True) # 无边框模式
self.win['bg'] = '#FFFFFF'
self.win.attributes("-transparentcolor", "#FFFFFF") # 设置纯白色为透明
# 画布
self.canvas = tk.Canvas(self.win, bg='#FFFFFF', highlightthickness=0)
self.canvas.pack(fill=tk.BOTH, expand=True)
self.canvas.bind("<Button-1>", self.__onClick)
# 绘制圆角矩形
self.canvas.create_oval(
0, 0, winR2, winR2, fill=bgColor, outline='')
self.canvas.create_oval(
winW-winR2, 0, winW, winR2, fill=bgColor, outline='')
self.canvas.create_oval(
winW-winR2, winH-winR2, winW, winH, fill=bgColor, outline='')
self.canvas.create_oval(
0, winH-winR2, winR2, winH, fill=bgColor, outline='')
self.canvas.create_rectangle(
winR, 0, winW-winR, winH, fill=bgColor, outline='')
self.canvas.create_rectangle(
0, winR, winW, winH-winR, fill=bgColor, outline='')
# 绘制叉叉进度条
self.prog = self.canvas.create_arc(winW-xxS1-xxS2, xxS2, winW-xxS2, xxS1+xxS2,
fill='', outline='gray', width=1, style='arc',
start=90, extent=0)
# self.canvas.create_text(
# winW-xxS2-int(xxS1/2), xxS2+int(xxS1/2), anchor='center',
# font=('Microsoft YaHei', xxS1-xxS2), text='×')
# 绘制文本
self.text1 = self.canvas.create_text(
*teP1, anchor='nw', font=('Microsoft YaHei', teS1),
text='')
self.text2 = self.canvas.create_text(
*teP2, anchor='nw', font=('Microsoft YaHei', teS2), fill='#666',
text='')

@staticmethod
def __easing(i): # 0-1之间的缓动函数,越靠近0增长越快
return 1+(i-1)*(i-1)*(i-1)

def __actionStart(self, t=popTime): # 开始弹出动画
if t <= 0:
self.state = State.showing # 状态:显示中
return
p = (popTime-t)/popTime # 0→1
p = self.__easing(p) # 缓动
y = winPosY * p
winX = int((self.win.winfo_screenwidth() - winW) / 2) # 位置:屏幕中下
winY = int(self.win.winfo_screenheight() - winH - y)
self.win.geometry(f'{winW}x{winH}+{winX}+{winY}') # 移动窗口
self.win.attributes("-alpha", p) # 透明度
self.afters[0] = self.win.after(
frameTime, lambda: self.__actionStart(t-frameTime/1000))

def __actionEnd(self, t=popTime): # 结束弹回动画
if t <= 0:
self.close()
return
p = t/popTime # 1→0
p = self.__easing(p) # 缓动
y = winPosY * p
winX = int((self.win.winfo_screenwidth() - winW) / 2) # 位置:屏幕中下
winY = int(self.win.winfo_screenheight() - winH - y)
self.win.geometry(f'{winW}x{winH}+{winX}+{winY}') # 移动窗口
self.win.attributes("-alpha", p) # 透明度
self.afters[1] = self.win.after(
frameTime, lambda: self.__actionEnd(t-frameTime/1000))

def __actionCountdown(self, t=downTime): # 倒计时动画
if t <= 0:
self.__actionEnd()
return
extent = 360 * (t/downTime) # 360→0
if extent < 5:
extent = 0
self.canvas.itemconfig(self.prog, extent=extent)
self.afters[2] = self.win.after(
frameTime, lambda: self.__actionCountdown(t-frameTime/1000))

def __onClick(self, *e): # 点击事件
self.__afterCancel() # 取消所有运行中的计时器
self.canvas.itemconfig(self.prog, extent=0) # 倒计时复位
self.__actionEnd() # 动画关闭

def __afterCancel(self): # 取消所有运行中的计时器
for i, a in enumerate(self.afters):
if a:
self.win.after_cancel(a)
self.afters[i] = None

def show(self, title, message):
'''显示一条消息。'''
if not self.win:
self.__initWin()
if not self.state == State.none:
self.close() # 关闭上一条消息
self.state = State.starting # 状态:弹出中
# 修改文字
title = title.replace('\n', '')
message = message.replace('\n', '')
self.canvas.itemconfig(self.text1, text=title)
self.canvas.itemconfig(self.text2, text=message)
# 恢复窗口
self.win.attributes('-topmost', 1) # 设置层级最前
self.win.state('normal') # 恢复前台状态
# 弹出动画
self.__actionStart()
# 启动倒计时动画
if downTime > 0:
self.__actionCountdown()

def close(self):
'''关闭当前通知'''
if self.state == State.none:
return
self.win.withdraw() # 隐藏消息
self.canvas.itemconfig(self.prog, extent=0) # 初始化进度条
self.__afterCancel() # 取消所有运行中的计时器
self.state = State.none


NotifyWin = NotifyWindow()


def Notify(title, message):
if not Config.get('isNotify'):
return
NotifyWin.show(title, message)
5 changes: 5 additions & 0 deletions utils/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,11 @@ class WindowTopModeFlag():
'isSave': True,
'isTK': True,
},
'isNotify': { # T时启用消息弹窗
'default': False,
'isSave': True,
'isTK': True,
},
'isAutoStartup': { # T时已添加开机自启
'default': False,
'isSave': True,
Expand Down

0 comments on commit 7c37fc7

Please sign in to comment.