gwapi/tkinter_ui/tkinter_ui.py

326 lines
12 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import datetime
import os
import sys
sys.path.append(os.path.dirname(sys.path[0]))
import tkinter as tk
from tkinter import messagebox
from PIL import Image, ImageTk
from utils.config import config
from utils.tools import resource_path, get_version_info
from main import UpdateSource
import asyncio
import threading
import webbrowser
from about import AboutUI
from default import DefaultUI
from speed import SpeedUI
from prefer import PreferUI
from local import LocalUI
from multicast import MulticastUI
from hotel import HotelUI
from subscribe import SubscribeUI
from online_search import OnlineSearchUI
from epg import EpgUI
from utils.speed import check_ffmpeg_installed_status
import pystray
from service.app import run_service
class TkinterUI:
def __init__(self, root):
info = get_version_info()
self.root = root
self.name = info.get("name", "")
self.tray_icon = None
self.root.protocol("WM_DELETE_WINDOW", self.on_closing)
self.create_tray_icon()
self.root.title(self.name)
self.version = info.get("version", "")
self.about_ui = AboutUI()
self.default_ui = DefaultUI()
self.speed_ui = SpeedUI()
self.prefer_ui = PreferUI()
self.local_ui = LocalUI()
self.multicast_ui = MulticastUI()
self.hotel_ui = HotelUI()
self.subscribe_ui = SubscribeUI()
self.online_search_ui = OnlineSearchUI()
self.epg_ui = EpgUI()
self.update_source = UpdateSource()
self.update_running = False
self.result_url = None
self.now = None
def on_closing(self):
if messagebox.askyesno("提示",
"最小化至后台运行?",
icon="question",
default="yes",
detail="选择“是”将最小化至后台运行,选择“否”将退出程序"
):
self.root.withdraw()
if not self.tray_icon:
self.create_tray_icon()
else:
self.root.destroy()
def create_tray_icon(self):
image = Image.open(resource_path("static/images/favicon.ico"))
menu = (pystray.MenuItem("显示", self.restore_window, default=True), pystray.MenuItem("退出", self.exit_app))
self.tray_icon = pystray.Icon("name", image, self.name, menu)
threading.Thread(target=self.tray_icon.run, daemon=True).start()
def restore_window(self):
self.root.deiconify()
def exit_app(self):
self.tray_icon.stop()
self.root.destroy()
def view_result_link_callback(self, event):
webbrowser.open_new_tab(self.result_url)
def save_config(self):
config.save()
messagebox.showinfo("提示", "保存成功")
def change_state(self, state):
self.default_ui.change_entry_state(state=state)
self.speed_ui.change_entry_state(state=state)
self.prefer_ui.change_entry_state(state=state)
self.local_ui.change_entry_state(state=state)
self.multicast_ui.change_entry_state(state=state)
self.hotel_ui.change_entry_state(state=state)
self.subscribe_ui.change_entry_state(state=state)
self.online_search_ui.change_entry_state(state=state)
self.epg_ui.change_entry_state(state=state)
async def run_update(self):
self.update_running = not self.update_running
if self.update_running:
self.run_button.config(text="取消更新", state="normal")
self.change_state("disabled")
self.progress_bar["value"] = 0
self.progress_label.pack()
self.view_result_link.pack()
self.progress_bar.pack()
await self.update_source.start(self.update_progress)
else:
self.stop()
self.update_source.stop()
self.run_button.config(text="启动", state="normal")
self.change_state("normal")
self.progress_bar.pack_forget()
self.view_result_link.pack_forget()
self.progress_label.pack_forget()
def on_run_update(self):
if not self.update_running and (
config.open_filter_resolution or config.open_rtmp) and not check_ffmpeg_installed_status():
if messagebox.askyesno("提示",
"使用分辨率、推流相关功能需要安装FFmpeg为了实现更佳的观看体验\n是否前往官网下载?"):
return webbrowser.open("https://ffmpeg.org")
if self.now:
self.update_source.stop()
loop = asyncio.new_event_loop()
def run_loop():
asyncio.set_event_loop(loop)
loop.run_until_complete(self.run_update())
self.thread = threading.Thread(target=run_loop, daemon=True)
self.thread.start()
def stop(self):
asyncio.get_event_loop().stop()
def update_progress(self, title, progress, finished=False, url=None, now=None):
self.progress_bar["value"] = progress
self.now = now
if finished and now:
next_time = now + datetime.timedelta(hours=config.update_interval)
title += f", 🕒下次更新时间: {next_time:%Y-%m-%d %H:%M:%S}"
progress_text = f"{title}, 进度: {progress}%" if not finished else f"{title}"
self.progress_label["text"] = progress_text
self.root.update()
if finished:
self.run_button.config(text="定时更新中(重启)" if now else "启动", state="normal")
self.update_running = False
self.change_state("normal")
if url:
self.view_result_link.config(text=url)
self.result_url = url
def init_UI(self):
menu_bar = tk.Menu(self.root)
help_menu = tk.Menu(menu_bar, tearoff=0)
help_menu.add_command(
label="关于",
command=lambda: self.about_ui.init_ui(root=self.root, version=self.version),
)
menu_bar.add_cascade(label="帮助", menu=help_menu)
self.root.config(menu=menu_bar)
notebook = tk.ttk.Notebook(self.root)
notebook.pack(fill="both", padx=10, pady=5)
frame_default = tk.ttk.Frame(notebook)
frame_speed = tk.ttk.Frame(notebook)
frame_prefer = tk.ttk.Frame(notebook)
frame_local = tk.ttk.Frame(notebook)
frame_hotel = tk.ttk.Frame(notebook)
frame_multicast = tk.ttk.Frame(notebook)
frame_subscribe = tk.ttk.Frame(notebook)
frame_online_search = tk.ttk.Frame(notebook)
frame_epg = tk.ttk.Frame(notebook)
settings_icon_source = Image.open(
resource_path("static/images/settings_icon.png")
).resize((16, 16))
settings_icon = ImageTk.PhotoImage(settings_icon_source)
speed_icon_source = Image.open(
resource_path("static/images/speed_icon.png")
).resize((16, 16))
speed_icon = ImageTk.PhotoImage(speed_icon_source)
prefer_icon_source = Image.open(
resource_path("static/images/prefer_icon.png")
).resize((16, 16))
prefer_icon = ImageTk.PhotoImage(prefer_icon_source)
local_icon_source = Image.open(
resource_path("static/images/local_icon.png")
).resize((16, 16))
local_icon = ImageTk.PhotoImage(local_icon_source)
hotel_icon_source = Image.open(
resource_path("static/images/hotel_icon.png")
).resize((16, 16))
hotel_icon = ImageTk.PhotoImage(hotel_icon_source)
multicast_icon_source = Image.open(
resource_path("static/images/multicast_icon.png")
).resize((16, 16))
multicast_icon = ImageTk.PhotoImage(multicast_icon_source)
subscribe_icon_source = Image.open(
resource_path("static/images/subscribe_icon.png")
).resize((16, 16))
subscribe_icon = ImageTk.PhotoImage(subscribe_icon_source)
online_search_icon_source = Image.open(
resource_path("static/images/online_search_icon.png")
).resize((16, 16))
online_search_icon = ImageTk.PhotoImage(online_search_icon_source)
epg_icon_source = Image.open(
resource_path("static/images/epg_icon.png")
).resize((16, 16))
epg_icon = ImageTk.PhotoImage(epg_icon_source)
notebook.add(
frame_default, text="通用设置", image=settings_icon, compound=tk.LEFT
)
notebook.add(frame_speed, text="测速设置", image=speed_icon, compound=tk.LEFT)
notebook.add(frame_prefer, text="偏好设置", image=prefer_icon, compound=tk.LEFT)
notebook.add(
frame_local, text="本地源", image=local_icon, compound=tk.LEFT
)
notebook.add(
frame_subscribe, text="订阅源", image=subscribe_icon, compound=tk.LEFT
)
notebook.add(frame_hotel, text="酒店源", image=hotel_icon, compound=tk.LEFT)
notebook.add(
frame_multicast, text="组播源", image=multicast_icon, compound=tk.LEFT
)
notebook.add(
frame_online_search,
text="关键字搜索",
image=online_search_icon,
compound=tk.LEFT,
)
notebook.add(
frame_epg,
text="EPG",
image=epg_icon,
compound=tk.LEFT,
)
notebook.settings_icon = settings_icon
notebook.speed_icon = speed_icon
notebook.prefer_icon = prefer_icon
notebook.local_icon = local_icon
notebook.hotel_icon = hotel_icon
notebook.multicast_icon = multicast_icon
notebook.subscribe_icon = subscribe_icon
notebook.online_search_icon = online_search_icon
notebook.epg_icon = epg_icon
self.default_ui.init_ui(frame_default)
self.speed_ui.init_ui(frame_speed)
self.prefer_ui.init_ui(frame_prefer)
self.local_ui.init_ui(frame_local)
self.multicast_ui.init_ui(frame_multicast)
self.hotel_ui.init_ui(frame_hotel)
self.subscribe_ui.init_ui(frame_subscribe)
self.online_search_ui.init_ui(frame_online_search)
self.epg_ui.init_ui(frame_epg)
root_operate = tk.Frame(self.root)
root_operate.pack(fill=tk.X, pady=8, padx=120)
root_operate_column1 = tk.Frame(root_operate)
root_operate_column1.pack(side=tk.LEFT, fill=tk.Y)
root_operate_column2 = tk.Frame(root_operate)
root_operate_column2.pack(side=tk.RIGHT, fill=tk.Y)
self.save_button = tk.ttk.Button(
root_operate_column1, text="保存设置", command=self.save_config
)
self.save_button.pack(side=tk.LEFT, padx=4, pady=8)
self.run_button = tk.ttk.Button(
root_operate_column2, text="启动", command=self.on_run_update
)
self.run_button.pack(side=tk.LEFT, padx=4, pady=8)
root_progress = tk.Frame(self.root)
root_progress.pack(fill=tk.X)
self.progress_bar = tk.ttk.Progressbar(
root_progress, length=300, mode="determinate"
)
self.progress_bar.pack_forget()
self.progress_label = tk.Label(root_progress, text="进度: 0%")
self.progress_label.pack_forget()
self.view_result_link = tk.Label(
root_progress, text="", fg="blue", cursor="hand2"
)
self.view_result_link.bind(
"<Button-1>",
self.view_result_link_callback,
)
self.view_result_link.pack_forget()
def get_root_location(root):
screen_width = root.winfo_screenwidth()
screen_height = root.winfo_screenheight()
width = 620
height = 670
x = (screen_width / 2) - (width / 2)
y = (screen_height / 2) - (height / 2)
return (width, height, x, y)
if __name__ == "__main__":
root = tk.Tk()
tkinter_ui = TkinterUI(root)
tkinter_ui.init_UI()
screen_width = root.winfo_screenwidth()
screen_height = root.winfo_screenheight()
root.geometry("%dx%d+%d+%d" % get_root_location(root))
root.iconbitmap(resource_path("static/images/favicon.ico"))
root.after(0, config.copy("config"))
root.after(0, config.copy("utils/nginx-rtmp-win32"))
root.after(0, config.copy("output"))
if config.open_service:
root.after(0, threading.Thread(target=run_service, daemon=True).start())
root.mainloop()