From e6d30846d09cdac90d13c809d1f1669aa56e5c60 Mon Sep 17 00:00:00 2001 From: jiangxufeng Date: Mon, 2 Sep 2019 00:22:16 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E4=BA=8C=E7=BB=B4=E7=A0=81?= =?UTF-8?q?=E5=AF=BC=E5=85=A5=E9=85=8D=E7=BD=AE=E3=80=81=E4=BA=8C=E7=BB=B4?= =?UTF-8?q?=E7=A0=81=E5=88=86=E4=BA=AB=E9=85=8D=E7=BD=AE=EF=BC=8C=E4=BF=AE?= =?UTF-8?q?=E5=A4=8D=E8=8B=A5=E5=B9=B2bug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 15 +++- v2rayL-GUI/sub2conf_api.py | 3 + v2rayL-GUI/v2rayL_api.py | 18 ++++- v2rayL-GUI/v2rayL_threads.py | 2 +- v2rayL-GUI/v2rayLui.py | 142 ++++++++++++++++++++++++++++------- 5 files changed, 147 insertions(+), 33 deletions(-) diff --git a/README.md b/README.md index 2d6a577..7489615 100644 --- a/README.md +++ b/README.md @@ -24,10 +24,21 @@ v2ray linux 客户端1.1,使用pyqt5编写GUI界面,核心基于v2ray-core 目前程序可能存在一些bug但是没有测试出,若在使用过程中发现bug,请在issue中提交,以便改进。 # Usage + +## 安装 +``` +① 在release中下载最新版本的install.sh +``` +``` +② 运行 ./install.sh ``` -① 在release中下载最新版本压缩包,并解压缩 -② 在解压缩后目录运行 ./install.sh +## 更新 +``` +① 在release中下载最新版本的update.sh +``` +``` +② 运行 ./update.sh ``` # 首页展示 diff --git a/v2rayL-GUI/sub2conf_api.py b/v2rayL-GUI/sub2conf_api.py index e9f8036..af7fbf5 100755 --- a/v2rayL-GUI/sub2conf_api.py +++ b/v2rayL-GUI/sub2conf_api.py @@ -59,6 +59,9 @@ def b642conf(self, prot, tp, b64str): region = string[1] ret["prot"] = prot + if tp == 0: + if region in self.saved_conf["subs"]: + region = region + "_local" self.saved_conf[["local", "subs"][tp]][region] = ret def setconf(self, region): diff --git a/v2rayL-GUI/v2rayL_api.py b/v2rayL-GUI/v2rayL_api.py index 08cb589..0293ae3 100755 --- a/v2rayL-GUI/v2rayL_api.py +++ b/v2rayL-GUI/v2rayL_api.py @@ -75,8 +75,21 @@ def update(self, url): pickle.dump((self.current, url, self.auto), jf) else: - with open("/etc/v2rayL/current", "wb") as jf: - pickle.dump((self.current, None, False), jf) + if self.current in self.subs.saved_conf["subs"]: + try: + self.disconnect() + except: + pass + + with open("/etc/v2rayL/current", "wb") as jf: + pickle.dump(("未连接至VPN", None, False), jf) + + else: + with open("/etc/v2rayL/current", "wb") as jf: + pickle.dump((self.current, None, False), jf) + + with open("/etc/v2rayL/data", "wb") as f: + pickle.dump({"local": self.subs.saved_conf["local"], "subs": {}}, f) def addconf(self, uri): self.subs = Sub2Conf(conf_url=uri) @@ -94,7 +107,6 @@ def ping(self, addr): raise MyException("测试超时") - if __name__ == '__main__': v = V2rayL() # t = subprocess.getoutput(["sudo systemctl status v2rayL.service"]) diff --git a/v2rayL-GUI/v2rayL_threads.py b/v2rayL-GUI/v2rayL_threads.py index a306c94..73dfbb6 100755 --- a/v2rayL-GUI/v2rayL_threads.py +++ b/v2rayL-GUI/v2rayL_threads.py @@ -96,7 +96,7 @@ def run(self): class PingThread(QThread): """ - 断开连接线程 + 测试延时线程 """ sinOut = pyqtSignal(tuple) diff --git a/v2rayL-GUI/v2rayLui.py b/v2rayL-GUI/v2rayLui.py index ce14d2c..e8f0191 100644 --- a/v2rayL-GUI/v2rayLui.py +++ b/v2rayL-GUI/v2rayLui.py @@ -2,6 +2,7 @@ # Author: Suummmmer # Date: 2019-08-13 +import requests from PyQt5.QtWidgets import ( QLabel, QAbstractItemView, @@ -26,13 +27,16 @@ qApp ) -from PyQt5.QtGui import QStandardItemModel, QStandardItem, QIcon, QStatusTipEvent, QCursor +from PyQt5.QtGui import QStandardItemModel, QStandardItem, QIcon, QStatusTipEvent, QPixmap, QCursor from PyQt5.QtCore import Qt, QSize, QRect, QCoreApplication, QMetaObject from v2rayL_api import V2rayL, MyException from datetime import datetime +import pyzbar.pyzbar as pyzbar +from PIL import Image from v2rayL_threads import ConnectThread, DisConnectThread, UpdateSubsThread, PingThread + class Ui_MainWindow(object): def setupUi(self, MainWindow): MainWindow.setObjectName("MainWindow") @@ -56,6 +60,8 @@ def setupUi(self, MainWindow): self.menu.setObjectName("menu") self.menu_3 = QMenu(self.menu) self.menu_3.setObjectName("menu_3") + self.menu_1 = QMenu(self.menu) + self.menu_1.setObjectName("menu_1") self.menu_4 = QMenu(self.menubar) self.menu_4.setObjectName("menu_4") self.menu_5 = QMenu(self.menu_4) @@ -83,8 +89,6 @@ def setupUi(self, MainWindow): self.action.setObjectName("action") self.action_2 = QAction(MainWindow) self.action_2.setObjectName("action_2") - self.action_4 = QAction(MainWindow) - self.action_4.setObjectName("action_4") self.action_5 = QAction(MainWindow) self.action_5.setObjectName("action_5") self.action_5.setShortcut('Ctrl+Q') @@ -99,27 +103,32 @@ def setupUi(self, MainWindow): self.action_9.setObjectName("action_9") self.action_10 = QAction(MainWindow) self.action_10.setObjectName("action_10") - self.action_10.setShortcut('F3') - self.action_11 = QAction(MainWindow) - self.action_11.setObjectName("action_11") - self.action_11.setShortcut('F4') + self.action_10.setShortcut('F1') self.action_24 = QAction(MainWindow) self.action_24.setObjectName("action_24") - self.action_24.setShortcut('F1') + self.action_24.setShortcut('F2') self.action_25 = QAction(MainWindow) self.action_25.setObjectName("action_25") - self.action_25.setShortcut('F2') + self.action_25.setShortcut('F3') self.action_26 = QAction(MainWindow) self.action_26.setObjectName("action_26") self.action_26.setShortcut('Ctrl+H') + self.action_27 = QAction(MainWindow) + self.action_27.setObjectName("action_27") + self.action_27.setShortcut('F4') + self.action_28 = QAction(MainWindow) + self.action_28.setObjectName("action_28") + self.action_28.setShortcut('F5') + self.menu.addAction(self.action_10) + self.menu.addSeparator() self.menu_3.addAction(self.action_24) self.menu_3.addAction(self.action_25) self.menu.addAction(self.menu_3.menuAction()) self.menu.addSeparator() - self.menu.addAction(self.action_10) - self.menu.addSeparator() - self.menu.addAction(self.action_11) + self.menu_1.addAction(self.action_27) + self.menu_1.addAction(self.action_28) + self.menu.addAction(self.menu_1.menuAction()) self.menu.addSeparator() self.menu_5.addAction(self.action_8) self.menu_5.addAction(self.action_9) @@ -149,23 +158,25 @@ def retranslateUi(self, MainWindow): MainWindow.setWindowTitle(_translate("MainWindow", "V2rayL")) MainWindow.setWindowIcon(QIcon("/etc/v2rayL/images/logo.png")) self.menu.setTitle(_translate("MainWindow", "配置")) - self.menu_3.setTitle(_translate("MainWindow", "添加配置")) + self.menu_3.setTitle(_translate("MainWindow", "添加")) self.menu_4.setTitle(_translate("MainWindow", "订阅")) self.menu_5.setTitle(_translate("MainWindow", "自动更新")) self.menu_2.setTitle(_translate("MainWindow", "帮助")) + self.menu_1.setTitle(_translate("MainWindow", "分享")) self.toolBar.setWindowTitle(_translate("MainWindow", "toolBar")) self.action.setText(_translate("MainWindow", "订阅设置")) self.action_2.setText(_translate("MainWindow", "更新订阅")) - self.action_4.setText(_translate("MainWindow", "二维码获取配置")) self.action_5.setText(_translate("MainWindow", "地址设置")) self.action_6.setText(_translate("MainWindow", "更新订阅")) self.action_8.setText(_translate("MainWindow", "开启")) self.action_9.setText(_translate("MainWindow", "关闭")) - self.action_10.setText(_translate("MainWindow", "导出当前配置")) - self.action_11.setText(_translate("MainWindow", "分享当前配置")) + self.action_10.setText(_translate("MainWindow", "导出")) self.action_24.setText(_translate("MainWindow", "通过链接")) self.action_25.setText(_translate("MainWindow", "通过二维码")) self.action_26.setText(_translate("MainWindow", "说明")) + self.action_27.setText(_translate("MainWindow", "链接分享")) + self.action_28.setText(_translate("MainWindow", "二维码分享")) + class Ui_Subs_Dialog(object): def setupUi(self, Dialog): @@ -233,6 +244,30 @@ def retranslateUi(self, Dialog): self.pushButton_2.setText(_translate("Dialog", "取消")) +class Ui_Qr_Dialog(object): + def setupUi(self, Dialog): + Dialog.setObjectName("Dialog") + Dialog.resize(218, 218) + Dialog.setMinimumSize(QSize(218, 218)) + Dialog.setMaximumSize(QSize(218, 218)) + self.gridLayout = QGridLayout(Dialog) + self.gridLayout.setObjectName("gridLayout") + self.label = QLabel(Dialog) + self.label.setEnabled(True) + self.label.setMinimumSize(QSize(200, 200)) + self.label.setMaximumSize(QSize(200, 200)) + self.label.setText("") + self.label.setObjectName("label") + self.gridLayout.addWidget(self.label, 0, 0, 1, 1) + + self.retranslateUi(Dialog) + QMetaObject.connectSlotsByName(Dialog) + + def retranslateUi(self, Dialog): + _translate = QCoreApplication.translate + Dialog.setWindowTitle(_translate("Dialog", "二维码分享配置")) + + class CenterDelegate(QItemDelegate): def __init__(self, parent=None): QItemDelegate.__init__(self, parent) @@ -310,6 +345,11 @@ def __init__(self, parent=None): self.confui = QDialog() self.confs_child_ui = Ui_Conf_Dialog() self.confs_child_ui.setupUi(self.confui) + # 二维码分享配置窗口 + self.qr_ui = QDialog() + self.qr_child_ui = Ui_Qr_Dialog() + self.qr_child_ui.setupUi(self.qr_ui) + self.status_format = "当前状态: {}\t\t\t\t\t\t自动更新: {}" # 获取api操作 self.v2rayL = V2rayL() @@ -354,10 +394,12 @@ def __init__(self, parent=None): self.action_6.triggered.connect(self.update_subs) # 更新订阅 self.action_8.triggered.connect(self.enable_auto_update) # 开启自动更新订阅 self.action_9.triggered.connect(self.disable_auto_update) # 关闭自动更新订阅 - self.action_24.triggered.connect(self.confs_ui_show) # 显示配置窗口 self.action_10.triggered.connect(self.output_conf) # 导出配置文件 - self.action_11.triggered.connect(self.output_conf_by_uri) # 生成分享链接 + self.action_24.triggered.connect(self.confs_ui_show) # 显示配置窗口 + self.action_25.triggered.connect(self.get_conf_from_qr) # 通过二维码导入 self.action_26.triggered.connect(self.help) # 显示帮助说明信息 + self.action_27.triggered.connect(self.output_conf_by_uri) # 生成分享链接 + self.action_28.triggered.connect(self.output_conf_by_qr) # 生成分享二维码 self.subs_child_ui.pushButton_2.clicked.connect(self.subs_ui_hide) # 关闭订阅地址窗口 self.confs_child_ui.pushButton_2.clicked.connect(self.confs_ui_hide) # 显示配置窗口 self.subs_child_ui.pushButton.clicked.connect(self.change_subs_addr) # 更新订阅操作 @@ -372,19 +414,18 @@ def __init__(self, parent=None): self.update_subs_start.sinOut.connect(self.alert) # 得到反馈 self.ping_ui.triggered.connect(self.start_ping_th) # 绑定ping程序 self.ping_start.sinOut.connect(self.alert) # 得到反馈 - self.CN.triggered.connect(self.start_conn_th) - self.DISCN.triggered.connect(self.end_conn_th) - self.customContextMenuRequested.connect(self.rightMenuShow) - + self.CN.triggered.connect(self.start_conn_th) # 右键菜单连接绑定 + self.DISCN.triggered.connect(self.end_conn_th) # 右键菜单断开连接绑定 + self.customContextMenuRequested.connect(self.rightMenuShow) # 显示右键菜单 # 设置最小化到托盘 self.tray() def help(self): QMessageBox.about(self, "说明", - self.tr("1. v2rayL当前版本:v1.1\n" \ - "2. github地址:https://github.com/jiangxufeng/v2rayL\n" \ - "3. 目前支持协议有:Vmess、shadowsocks\n4. 目前仅支持通过分享链接导入配置\n" \ - "5. 双击选中配置可直接进行连接\n6. 程序可能存在为测试到的Bug,使用过程中发现Bug请在github提交")) + self.tr("1. v2rayL当前版本:v1.1.4\n" + "2. github地址:https://github.com/jiangxufeng/v2rayL\n" + "3. 目前支持协议有:Vmess、shadowsocks\n4. 支持通过分享链接、二维码导入和分享配置\n" + "5. 双击选中配置可直接进行连接\n6. 程序可能存在未测试到的Bug,使用过程中发现Bug请在github提交")) def tray(self): # 创建托盘程序 @@ -470,7 +511,9 @@ def change_subs_addr(self): self.update_addr_start.v2rayL = self.v2rayL self.update_addr_start.subs_child_ui = self.subs_child_ui if not url: - choice = QMessageBox.warning(self, "订阅地址更新", self.tr("当前订阅地址为空,继续则删除订阅地址,是否继续?"), + choice = QMessageBox.warning(self, "订阅地址更新", self.tr("当前订阅地址为空," + "继续则删除订阅地址,同时会删除所有订阅配置," + "是否继续?"), QMessageBox.Ok | QMessageBox.Cancel, QMessageBox.Cancel) if choice == QMessageBox.Ok: self.statusbar.showMessage("正在更新订阅地址......") @@ -492,6 +535,7 @@ def update_subs(self): """ self.update_subs_start.v2rayL = self.v2rayL self.update_subs_start.subs_child_ui = None + self.statusbar.showMessage("正在更新订阅......") self.update_subs_start.start() def get_conf_from_uri(self): @@ -517,6 +561,27 @@ def get_conf_from_uri(self): self.display_all_conf() self.confs_child_ui.lineEdit.setText("") + def get_conf_from_qr(self): + """ + 从二维码导入配置 + :return: + """ + fname, ok = QFileDialog.getOpenFileName(self, '选择二维码图片', '/home', 'Image files(*.jpg *.png)') + if ok: + try: + barcode = pyzbar.decode(Image.open(fname)) + except: + QMessageBox.critical(self, "二维码解析错误", "无法解析该二维码。") + else: + try: + self.v2rayL.addconf(barcode[0].data.decode("utf-8")) + except MyException as e: + QMessageBox.critical(self, "错误", self.tr(e.args[0])) + else: + QMessageBox.information(self, "完成", self.tr("配置添加成功!")) + self.v2rayL = V2rayL() + self.display_all_conf() + def del_conf(self): """ 移除一个配置 @@ -657,6 +722,29 @@ def output_conf_by_uri(self): ret = self.v2rayL.subs.conf2b64(region) QMessageBox.information(self, "分享链接", self.tr(ret)) + def output_conf_by_qr(self): + """ + 输出分享二维码 + :return: + """ + row = self.tableView.currentIndex().row() + region = self.tableView.model().item(row, 0).text() + ret = self.v2rayL.subs.conf2b64(region) + # 生成二维码 + url = "http://api.k780.com:88/?app=qr.get&data={}".format(ret) + try: + req = requests.get(url) + if req.status_code == 200: + qr = QPixmap() + qr.loadFromData(req.content) + self.qr_child_ui.label.setPixmap(qr) + self.qr_child_ui.label.setScaledContents(True) + self.qr_ui.show() + else: + QMessageBox.critical(self, "错误", self.tr("服务错误,可能原因:调用API发生错误")) + except: + QMessageBox.critical(self, "错误", self.tr("服务错误,请将错误在github中提交")) + def event(self, QEvent): """ 使得statusbar始终显示内容