From 96518ebc5df76bcf6bd2651760c84f64c28c2b98 Mon Sep 17 00:00:00 2001 From: liyp Date: Sun, 24 Dec 2023 20:45:07 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E5=87=A0=E4=B8=AA=E5=8A=9F?= =?UTF-8?q?=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 4 +- main/main.py | 176 +++++++++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 166 insertions(+), 14 deletions(-) diff --git a/.gitignore b/.gitignore index b694934..c12e0a4 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,3 @@ -.venv \ No newline at end of file +.venv +main/test.py +main/test1.py \ No newline at end of file diff --git a/main/main.py b/main/main.py index 49526d7..1e16ad2 100644 --- a/main/main.py +++ b/main/main.py @@ -1,14 +1,25 @@ import sys +import platform import requests from PyQt5.QtWidgets import QApplication, QMainWindow, QSystemTrayIcon, QMenu, QAction,QActionGroup from PyQt5.QtGui import QIcon - +from PyQt5.QtCore import QTimer,pyqtSignal,QThread +import json +import aiohttp +import asyncio class TrayIconExample(QMainWindow): + + speed_data = pyqtSignal(dict) def __init__(self): super(TrayIconExample, self).__init__() self.init_ui() + # 启动异步任务 + + self.worker_thread = WorkerThread(self) + self.worker_thread.data_ready.connect(self.update_speed_data) + self.worker_thread.start() def init_ui(self): self.setWindowTitle('Clash Tray') @@ -23,11 +34,39 @@ class TrayIconExample(QMainWindow): exit_action = QAction('退出', self) self.tun_mode = QAction('TUN模式', self) self.tun_mode.setCheckable(True) + # 获取环境变量 + self.copy_env=QAction('复制环境变量',self) - # 创建一个 QActionGroup + self.more_menu=QMenu('更多',self) + self.upload_speed=QAction('⬆️上传速度',self) + self.download_speed=QAction('⬇️下载速度',self) + self.upload_speed.setCheckable(False) + self.upload_speed.setEnabled(False) + self.download_speed.setCheckable(False) + self.download_speed.setEnabled(False) + + # self.timer = QTimer(self) + # self.timer.timeout.connect(self.update_data) + # self.timer.start(1000) # 定时器间隔为1000毫秒(1秒) + + # 更多 子菜单项设置 + self.version=self.get_version() + self.show_version = QAction('内核版本:'+self.version, self) + self.show_version.setCheckable(False) + self.show_version.setEnabled(False) + self.restart_core=QAction('重启Clash',self) + + self.more_menu.addAction(self.restart_core) + self.more_menu.addAction(self.show_version) + + + self.restart_core.triggered.connect(self.restart_clash) + + + # 创建代理模式 QActionGroup proxy_mode_group = QActionGroup(self) proxy_mode_group.setExclusive(True) # 设置为单选模式 - + # 设置代理模式菜单 self.direct_mode=QAction('直连模式',self) self.rule_mode=QAction('规则模式',self) self.global_mode=QAction('全局模式',self) @@ -39,12 +78,20 @@ class TrayIconExample(QMainWindow): proxy_mode_group.addAction(self.rule_mode) proxy_mode_group.addAction(self.global_mode) + self.tun_mode.triggered.connect(self.change_tun_mode) exit_action.triggered.connect(self.exit_application) + # 将菜单添加到托盘图标 context_menu.addAction(self.tun_mode) context_menu.addMenu(proxy_mode) - - context_menu.addAction(exit_action) + context_menu.addAction(self.copy_env) + context_menu.addMenu(self.more_menu) + context_menu.addActions([self.upload_speed,self.download_speed,exit_action]) proxy_mode.addActions([self.direct_mode,self.rule_mode,self.global_mode]) + + self.copy_env.triggered.connect(self.copy_env_var) + + + self.direct_mode.triggered.connect(lambda:self.set_proxy_mode(0)) self.rule_mode.triggered.connect(lambda:self.set_proxy_mode(1)) self.global_mode.triggered.connect(lambda:self.set_proxy_mode(2)) @@ -57,7 +104,14 @@ class TrayIconExample(QMainWindow): tray_icon.show() self.get_config() - self.setGeometry(100, 100, 800, 600) + self.setGeometry(200, 200, 1200, 800) + + def restart_clash(self): + url,port=self.get_connect() + data={'path': '','payload': ''} + response=requests.post(url=url+':'+port+'/restart').json() + if response['status']=='ok': + print('重启成功') def get_connect(self,url=None,port=None): url='http://127.0.0.1' @@ -83,34 +137,130 @@ class TrayIconExample(QMainWindow): self.tun_mode.setChecked(True) else: self.tun_mode.setChecked(False) + return response + + def get_version(self): + url,port=self.get_connect() + response=requests.get(url=url+':'+port+'/version') + if response.status_code==200: + res=response.json() + print('version',res) + return res['version'] def set_proxy_mode(self,type): mode=None if type==0: mode='direct' - # self.rule_mode.setChecked(True) elif type==1: mode='rule' - # self.rule_mode.setChecked(True) elif type==2: mode='global' - # self.global_mode.setChecked(True) url,port=self.get_connect() data = {"mode": mode} response=requests.patch(url=url+':'+port+'/configs?force=true',json=data) if response.status_code==204: self.get_config() + def change_tun_mode(self): + url,port=self.get_connect() + print(self.tun_mode.isChecked()) + data = {"tun": {"enable": False if self.tun_mode.isChecked() else True}} + print(data) + response=requests.patch(url=url+':'+port+'/configs?force=true',json=data) + if response.status_code==204: + self.get_config() + + def copy_env_var(self): + sys_info=platform.uname() + clipboard = QApplication.clipboard() + env_var='' + # print(sys_info.system) + if sys_info.system == ('Linux' or 'Darwin'): + env_var='export https_proxy=http://127.0.0.1:7890 http_proxy=http://127.0.0.1:7890 all_proxy=socks5://127.0.0.1:7890' + elif sys_info.system == 'Windows': + env_var='setx https_proxy=http://127.0.0.1:7890\nsetx http_proxy=http://127.0.0.1:7890\nsetx all_proxy=socks5://127.0.0.1:7890' + + # print(env_var) + clipboard.setText(env_var) + + def update_speed_data(self,data): + print(data) + upload=data['up'] + down=data['down'] - - - - + if upload /1024<=1024 or down /1024<=1024: + upload=round(upload/1024,2) + down=round(down/1024,2) + self.upload_speed.setText('上传速度:'+str(upload)+'KB/s') + self.download_speed.setText('下载速度:'+str(down)+'KB/s') + else: + upload=round(upload/1024/1024,2) + down=round(down/1024/1024,2) + self.upload_speed.setText('上传速度:'+str(upload)+'MB/s') + self.download_speed.setText('下载速度:'+str(down)+'MB/s') + pass def exit_application(self): QApplication.quit() + # async def fetch_and_output_data(self): + # url = "http://127.0.0.1:9090/traffic" # 替换为实际的 API 地址 + # async with aiohttp.ClientSession() as session: + # async with session.get(url, timeout=None) as response: # timeout=None 表示不设置超时 + # while True: + # partial_data = await response.content.read(100) # 每次读取100字节的数据示例 + + # if not partial_data: + # break # 如果没有更多数据,退出循环 + + # # 在此处可以进行异步加载部分数据的操作,例如使用 Qt 的异步加载机制 + # # print("Partial data:", partial_data) + + # # 将字节数据转换为字符串 + # partial_data_str = partial_data.decode('utf-8') + + # print(partial_data_str) + + # # 将字符串解析为 JSON 对象 + # try: + # partial_data_json = json.loads(partial_data_str) + # print("Partial data:", partial_data_json) + # self.speed_data.emit(partial_data_json) + # except json.JSONDecodeError as e: + # print(f"Error decoding JSON: {e}") + + # await asyncio.sleep(1) # 休眠1秒,模拟每秒输出一部分数据 + +class WorkerThread(QThread): + data_ready = pyqtSignal(dict) + + def run(self): + loop = asyncio.new_event_loop() + asyncio.set_event_loop(loop) + loop.run_until_complete(self.fetch_and_output_data()) + + async def fetch_and_output_data(self): + url = "http://127.0.0.1:9090/traffic" + async with aiohttp.ClientSession() as session: + async with session.get(url, timeout=None) as response: + while True: + partial_data = await response.content.read(100) + + if not partial_data: + break + + partial_data_str = partial_data.decode('utf-8') + + try: + partial_data_json = json.loads(partial_data_str) + self.data_ready.emit(partial_data_json) + except json.JSONDecodeError as e: + print(f"解析 JSON 时出错:{e}") + + await asyncio.sleep(1) + + if __name__ == '__main__': app = QApplication(sys.argv) window = TrayIconExample()