🌐【开源工具】一键解决使用代理后无法访问浏览器网页问题 - 基于PyQt5的智能代理开关工具开发全攻略

在这里插入图片描述
请添加图片描述

🌈 个人主页:创客白泽 - CSDN博客
🔥 系列专栏:🐍《Python开源项目实战》
💡 热爱不止于代码,热情源自每一个灵感闪现的夜晚。愿以开源之火,点亮前行之路。
🐋 希望大家多多支持,我们一起进步!
👍 🎉如果文章对你有帮助的话,欢迎 点赞 👍🏻 评论 💬 收藏 ⭐️ 加关注+💗分享给更多人哦

请添加图片描述

在这里插入图片描述

📌 概述:代理访问的痛点与解决方案

在日常开发和学习中,我们经常需要使用代理访问国际资源。但Windows系统代理设置存在几个典型痛点:

  1. 代理频繁失效需要手动切换
  2. 设置路径深(控制面板→Internet选项→连接→局域网设置)
  3. 状态不可视化,无法快速判断当前代理状态

本文介绍一款基于PyQt5开发的可视化代理开关工具,具有以下核心优势:

  • 一键切换系统代理状态
  • 美观的动画可视化效果
  • 实时状态反馈
  • 便携式设计(单文件运行)

🛠️ 功能特性

核心功能

  • 注册表代理状态实时检测
  • 管理员权限自动检测
  • 系统设置变更通知
  • 拖拽式无边框窗口

增强特性

  • 平滑过渡动画(QEasingCurve控制)
  • 自适应UI布局
  • 多显示器DPI适配
  • 跨平台兼容处理(Windows/macOS)

🎥 效果展示

在这里插入图片描述
在这里插入图片描述

图1:工具运行效果(开启/关闭状态对比对应的网络代理开启变化)

🧰 开发环境准备

基础环境

  • Python 3.8+
  • PyQt5 5.15
  • Windows 10/11(需管理员权限)

安装依赖

pip install pyqt5 win32ctypes ctypes

🚀 使用说明

基础使用

  1. 直接运行程序(建议管理员权限)
  2. 点击滑块切换代理状态
  3. 顶部拖拽区域可移动窗口
  4. 点击×按钮关闭程序

高级配置

修改代理服务器地址(需编辑注册表):

# 在setProxyState方法中添加以下代码
proxy_server = "127.0.0.1:1080"
winreg.SetValueEx(key, "ProxyServer", 0, winreg.REG_SZ, proxy_server)

🔍 代码深度解析

1. 自定义SwitchButton组件

class SwitchButton(QWidget):toggled = pyqtSignal(bool)def paintEvent(self, event):# 使用QLinearGradient实现渐变色效果gradient = QLinearGradient(bg_rect.topLeft(), bg_rect.bottomRight())if self._checked:gradient.setColorAt(0, QColor("#66BB6A"))  # 开启状态渐变色else:gradient.setColorAt(0, QColor("#E0E0E0"))  # 关闭状态渐变色

关键点分析

  • 通过pyqtProperty实现动画属性绑定
  • 使用QEasingCurve.OutQuad实现自然减速效果
  • 双状态绘制逻辑(开启/关闭)

2. 注册表操作核心逻辑

def setProxyState(self, enabled):try:with winreg.OpenKey(winreg.HKEY_CURRENT_USER, r"Software\Microsoft\Windows\CurrentVersion\Internet Settings", 0, winreg.KEY_WRITE) as key:winreg.SetValueEx(key, "ProxyEnable", 0, winreg.REG_DWORD, 1 if enabled else 0)# 关键!通知系统设置变更self.notifySystem()

注册表路径解析

HKEY_CURRENT_USER
└── Software└── Microsoft└── Windows└── CurrentVersion└── Internet Settings├── ProxyEnable (DWORD)└── ProxyServer (STRING)

3. 系统通知机制

def notifySystem(self):INTERNET_OPTION_SETTINGS_CHANGED = 39INTERNET_OPTION_REFRESH = 37ctypes.windll.Wininet.InternetSetOptionW(0, INTERNET_OPTION_SETTINGS_CHANGED, 0, 0)ctypes.windll.Wininet.InternetSetOptionW(0, INTERNET_OPTION_REFRESH, 0, 0)

技术原理
调用WinINet API通知系统代理设置已变更,避免需要重启浏览器才能生效

💾 完整源码下载

import sys
from PyQt5.QtWidgets import (QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout, QLabel, QPushButton, QFrame)
from PyQt5.QtCore import (Qt, QPropertyAnimation, QEasingCurve, QPoint, pyqtProperty, pyqtSignal)
from PyQt5.QtGui import QColor, QFont, QPainter, QBrush, QPen, QLinearGradient
import winreg
import ctypes
import platformclass SwitchButton(QWidget):toggled = pyqtSignal(bool)def __init__(self, parent=None):super().__init__(parent)self.setFixedSize(80, 40)self._checked = Falseself._circle_position = QPoint(5, 5)  # 明确初始化位置self._bg_color = QColor("#cccccc")self._circle_color = QColor("#ffffff")self._active_color = QColor("#4CAF50")self._animation = QPropertyAnimation(self, b"circle_position")self._animation.setDuration(200)self._animation.setEasingCurve(QEasingCurve.OutQuad)def paintEvent(self, event):painter = QPainter(self)painter.setRenderHint(QPainter.Antialiasing)# 绘制背景bg_rect = self.rect()gradient = QLinearGradient(bg_rect.topLeft(), bg_rect.bottomRight())if self._checked:gradient.setColorAt(0, QColor("#66BB6A"))gradient.setColorAt(1, QColor("#81C784"))else:gradient.setColorAt(0, QColor("#E0E0E0"))gradient.setColorAt(1, QColor("#BDBDBD"))painter.setBrush(QBrush(gradient))painter.setPen(Qt.NoPen)painter.drawRoundedRect(bg_rect, 20, 20)# 绘制圆形滑块painter.setBrush(QBrush(self._circle_color))painter.setPen(QPen(QColor("#999999"), 1))painter.drawEllipse(self._circle_position.x(), 5, 30, 30)@pyqtProperty(QPoint)def circle_position(self):return self._circle_position@circle_position.setterdef circle_position(self, pos):self._circle_position = posself.update()def mousePressEvent(self, event):if event.button() == Qt.LeftButton:self.toggle()def toggle(self, checked=None):if checked is None:self._checked = not self._checkedelse:self._checked = checked# 动画效果start_pos = QPoint(5, 5) if not self._checked else QPoint(45, 5)end_pos = QPoint(45, 5) if not self._checked else QPoint(5, 5)self._animation.stop()self._animation.setStartValue(start_pos)self._animation.setEndValue(end_pos)self._animation.start()self.update()self.toggled.emit(self._checked)def isChecked(self):return self._checkeddef setChecked(self, checked):self.toggle(checked)class ProxySwitchApp(QMainWindow):def __init__(self):super().__init__()self.setWindowTitle("网络代理开关")self.setFixedSize(180, 210)self.setWindowFlags(self.windowFlags() | Qt.FramelessWindowHint)self.setAttribute(Qt.WA_TranslucentBackground)self.initUI()self.initProxyState()def initUI(self):# 主窗口容器self.main_widget = QWidget()self.main_widget.setObjectName("mainWidget")self.setCentralWidget(self.main_widget)# 主布局layout = QVBoxLayout(self.main_widget)layout.setContentsMargins(20, 20, 20, 20)layout.setSpacing(20)# 标题栏title_bar = QWidget()title_bar.setFixedHeight(30)title_bar_layout = QHBoxLayout(title_bar)title_bar_layout.setContentsMargins(0, 0, 0, 0)self.title_label = QLabel("网络代理开关")self.title_label.setStyleSheet("font-size: 16px; font-weight: bold; color: #333333;")self.close_btn = QPushButton("×")self.close_btn.setFixedSize(30, 30)self.close_btn.setStyleSheet("""QPushButton {border: none;font-size: 18px;color: #777777;}QPushButton:hover {color: #ffffff;background-color: #E81123;border-radius: 15px;}""")self.close_btn.clicked.connect(self.close)title_bar_layout.addWidget(self.title_label)title_bar_layout.addStretch()title_bar_layout.addWidget(self.close_btn)# 开关控件switch_container = QWidget()switch_layout = QHBoxLayout(switch_container)switch_layout.setContentsMargins(0, 0, 0, 0)self.switch = SwitchButton()self.switch.toggled.connect(self.onSwitchToggled)self.status_label = QLabel()self.status_label.setStyleSheet("font-size: 14px; color: #555555;")self.status_label.setAlignment(Qt.AlignCenter)switch_layout.addStretch()switch_layout.addWidget(self.switch)switch_layout.addStretch()# 底部信息info_label = QLabel("点击滑块切换代理状态")info_label.setStyleSheet("font-size: 12px; color: #888888;")info_label.setAlignment(Qt.AlignCenter)# 添加到主布局layout.addWidget(title_bar)layout.addWidget(switch_container)layout.addWidget(self.status_label)layout.addWidget(info_label)layout.addStretch()# 设置样式self.main_widget.setStyleSheet("""#mainWidget {background-color: #ffffff;border-radius: 10px;border: 1px solid #dddddd;}""")def initProxyState(self):try:with winreg.OpenKey(winreg.HKEY_CURRENT_USER, r"Software\Microsoft\Windows\CurrentVersion\Internet Settings", 0, winreg.KEY_READ) as key:value, _ = winreg.QueryValueEx(key, "ProxyEnable")self.switch.setChecked(value == 1)self.updateStatusLabel(value == 1)except WindowsError:self.switch.setChecked(False)self.updateStatusLabel(False)def onSwitchToggled(self, checked):self.setProxyState(checked)self.updateStatusLabel(checked)def setProxyState(self, enabled):try:with winreg.OpenKey(winreg.HKEY_CURRENT_USER, r"Software\Microsoft\Windows\CurrentVersion\Internet Settings", 0, winreg.KEY_WRITE) as key:winreg.SetValueEx(key, "ProxyEnable", 0, winreg.REG_DWORD, 1 if enabled else 0)# 通知系统设置已更改self.notifySystem()return Trueexcept WindowsError as e:print(f"修改注册表失败: {e}")# 尝试以管理员身份运行if not self.isAdmin():self.showMessage("需要管理员权限", "请以管理员身份运行此程序")return Falsedef updateStatusLabel(self, enabled):if enabled:self.status_label.setText("代理状态: <span style='color:#4CAF50;'>已开启</span>")else:self.status_label.setText("代理状态: <span style='color:#F44336;'>已关闭</span>")def isAdmin(self):try:return ctypes.windll.shell32.IsUserAnAdmin()except:return Falsedef notifySystem(self):"""通知系统代理设置已更改"""if platform.system() == "Windows":INTERNET_OPTION_SETTINGS_CHANGED = 39INTERNET_OPTION_REFRESH = 37ctypes.windll.Wininet.InternetSetOptionW(0, INTERNET_OPTION_SETTINGS_CHANGED, 0, 0)ctypes.windll.Wininet.InternetSetOptionW(0, INTERNET_OPTION_REFRESH, 0, 0)def showMessage(self, title, message):from PyQt5.QtWidgets import QMessageBoxQMessageBox.information(self, title, message)def mousePressEvent(self, event):if event.button() == Qt.LeftButton:self.drag_pos = event.globalPos()event.accept()def mouseMoveEvent(self, event):if event.buttons() == Qt.LeftButton and hasattr(self, 'drag_pos'):self.move(self.pos() + event.globalPos() - self.drag_pos)self.drag_pos = event.globalPos()event.accept()if __name__ == "__main__":app = QApplication(sys.argv)# 设置全局字体font = QFont("Microsoft YaHei" if platform.system() == "Windows" else "PingFang SC")app.setFont(font)window = ProxySwitchApp()window.show()sys.exit(app.exec_())

🏆 进阶优化方向

1. 多代理配置预设

self.proxy_presets = {"SSR": "127.0.0.1:1080","Clash": "127.0.0.1:7890","V2Ray": "127.0.0.1:10808"
}

2. 系统托盘支持

from PyQt5.QtWidgets import QSystemTrayIcon, QMenudef createTrayIcon(self):tray = QSystemTrayIcon(self)menu = QMenu()# 添加菜单项...tray.setContextMenu(menu)tray.show()

3. 自动代理配置(PAC)支持


winreg.SetValueEx(key, "AutoConfigURL", 0, winreg.REG_SZ, pac_url)

📚 技术栈深度

PyQt5动画系统

  • QPropertyAnimation:属性动画核心类
  • QEasingCurve:提供30+种缓动曲线
  • 属性绑定机制:@pyqtProperty装饰器

Windows注册表安全

  • 权限管理:KEY_WRITE需要管理员权限
  • 异常处理:WindowsError捕获
  • 64/32位兼容:KEY_WOW64_64KEY标志

🧐 常见问题解答

Q:为什么修改后代理不立即生效?
A:需要调用InternetSetOptionW通知系统,部分浏览器可能需要手动刷新

Q:如何支持 socks5 代理?
A:需修改注册表ProxyServer值为socks=127.0.0.1:1080

Q:程序无法修改注册表?
A:右键以管理员身份运行,或添加UAC清单文件

🎯 总结与展望

本文开发的代理开关工具解决了以下核心问题:

  1. 将复杂的注册表操作可视化
  2. 提供状态即时反馈
  3. 通过动画提升用户体验

未来扩展方向

  • 增加代理延迟测试功能
  • 支持代理规则分组
  • 开发浏览器插件联动控制

技术思考:这种将系统底层配置可视化的思路,同样适用于其他系统设置管理(如环境变量、服务控制等),值得深入探索。


版权声明:本文代码部分采用MIT开源协议,转载请注明出处。技术交流欢迎评论区留言或私信作者。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/bicheng/86605.shtml
繁体地址,请注明出处:http://hk.pswp.cn/bicheng/86605.shtml
英文地址,请注明出处:http://en.pswp.cn/bicheng/86605.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

异步IO框架io_uring实现TCP服务器

一、io_uring介绍 io_uring是 Linux 于 2019 年加入到内核的一种新型异步 I/O 模型&#xff0c;io_uring 主要为了解决 原生AIO&#xff08;Native AIO&#xff09; 存在的一些不足之处。下面介绍一下原生 AIO 的不足之处&#xff1a; 系统调用开销大&#xff1a;提交 I/O 操作…

【docker】docker run参数说明

功能 拉起容器。 参数 -i&#xff0c;--interactive 保持容器标准输入放开&#xff0c;就算没有终端也放开。 可以理解为可以向容器内输入东西&#xff0c;比如&#xff1a; [rootlocalhost ~]# echo 111 | docker run -i yaxin:1.0 cat 111--cap-add 用于向容器添加特定的…

从0开始学习计算机视觉--Day04--损失函数

在上次学习中&#xff0c;我们知道了线性分类的函数是f(x,W),但并没有解释要怎么得到W权重矩阵的值&#xff0c;以及我们要怎么用训练数据来确定它的最优权重矩阵。在之前我们知道&#xff0c;假设用了10种类别的图片用于训练&#xff0c;将其中一种图片输入模型后&#xff0c;…

【V2.0】TPS-61088升压板-3.7V升压到9V电源板

优化一下上一版本的升压板&#xff1a; TPS-61088升压板-3.7V升压到9V电源板-CSDN博客 改动参考了官方的demo板 加了很多的电容&#xff0c;封装很大&#xff0c;同时去掉了AGND&#xff0c;直接使用一个GND。 补偿电路增加了一个47pF的电容。 EN引脚改用输入的电压分压来启…

基于DeepSeek搭建Dify智能助手国产化架构运行arm64

基于DeepSeek搭建Dify智能助手国产化架构运行arm64 基于DeepSeek搭建Dify智能助手案例介绍案例内容1 概述1.1 背景介绍1.2 适用对象1.3 案例时间1.4 案例流程1.5 资源总览 2.启动 Docker 容器没有的安装2.1没有Docker安装 3 云主机部署DeepSeek3.1 安装Ollama 4.安装Dify4.1Doc…

PyQtNode Editor 第一篇环境配置

PyQtNode Editor 以其独特的功能和灵活的扩展性&#xff0c;吸引了众多开发者的目光。 这篇博客作为系列开篇&#xff0c;将详细介绍开发 PyQtNode Editor 所需的基础环境、安装步骤&#xff0c;同时深入解读一段简单的 PyQt5 代码&#xff0c;为后续的开发工作奠定基础。 一…

Unity 脚本自动添加头部注释

&#x1f4dd; Unity Script Header 注释生成器 一个轻量、可配置的 Unity 编辑器工具&#xff0c;在创建 .cs 脚本时自动插入统一格式的注释头信息。 支持作者、邮箱、公司、地点、版权、描述等字段&#xff0c;所有信息都可通过 Project Settings 界面配置并动态开关。 &…

偏微分方程能量变化分析2

题目 问题 9. 考虑以下带有边界条件的偏微分方程&#xff08;PDE&#xff09;&#xff1a; u t t − c 2 u x x 0 , x > 0 , u_{tt} - c^2 u_{xx} 0, \quad x > 0, utt​−c2uxx​0,x>0, u ∣ x 0 0. u|_{x0} 0. u∣x0​0. 定义能量泛函&#xff1a; E ( t ) …

模型部署和推理架构学习笔记

一. 初步认识模型部署 1. 什么是ONNX&#xff1f; ONNX 就是一个 中间人 或 通用翻译器。它让你在喜欢的框架&#xff08;如 PyTorch&#xff09;里训练好模型后&#xff0c;能轻松地把它变成一种 标准格式。然后&#xff0c;这个标准格式的模型可以被 很多不同的工具和硬件 …

OpenCV CUDA模块设备层-----逐通道的正弦运算函数sin()

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 OpenCV 的 CUDA 模块&#xff08;cv::cudev&#xff09; 中的一个设备端数学函数&#xff0c;用于在 CUDA 核函数中对 uchar4 类型&#xff08;即…

人工智能-基础篇-2-什么是机器学习?(ML,监督学习,半监督学习,零监督学习,强化学习,深度学习,机器学习步骤等)

1、什么是机器学习&#xff1f; 机器学习&#xff08;Machine Learning, ML&#xff09;是人工智能的一个分支&#xff0c;是一门多领域交叉学科&#xff0c;涉及概率论、统计学、逼近论、凸分析等数学理论。其核心目标是让计算机通过分析数据&#xff0c;自动学习规律并构建模…

【deepseek】TCP/IP ISO 卸载

TCP/IP 全卸载中的 LSO&#xff08;Large Send Offload&#xff09; 和 LRO&#xff08;Large Receive Offload&#xff09; 是网卡硬件加速技术&#xff0c;其核心目标是 将 TCP/IP 协议栈的处理任务从 CPU 转移到网卡硬件&#xff0c;从而大幅降低 CPU 负载并提升网络性能。以…

抖音小程序支付错误码141211

前情 uni-app是我比较喜欢的跨平台框架&#xff0c;它能开发小程序/H5/APP(安卓/iOS)&#xff0c;重要的是对前端开发友好&#xff0c;自带的IDE让开发体验也挺棒的&#xff0c;公司项目就是主推uni-app 公司今年准备新开一个项目&#xff0c;但是对项目的未来和项目要做的规…

springcloud/springmvc协调作用传递验证信息

微服务架构的拆分&#xff0c;各模块之间使用feign组件来进行相互http转发通信。 前端与后端之间使用springcloud的网关来进行协调。 现在问题出现&#xff0c;用户的信息如何进行传递&#xff1f; 前端请求携带请求头&#xff0c;请求头中的authorization为携带的对应token…

Apache Flink Kafka 写连接器源码深度剖析

一、架构概述 Apache Flink 提供的 Kafka 写入连接器是实现与 Kafka 消息队列集成的关键组件&#xff0c;支持多种语义保证和灵活配置选项。本文将深入分析 Flink Kafka 写入连接器的源码实现&#xff0c;包括架构设计、核心类、事务机制和性能优化等方面。 1.1 整体架构 Fl…

强化学习理论基础:从Q-learning到PPO的算法演进(2)

文章目录 Policy gradient思想(REINFORCE算法)优势函数PPO(Proximal Policy Optimization)Policy gradient思想(REINFORCE算法) 下面我们来探讨一下Policy gradient策略,也就是REINFORCE算法。 在玩剪刀石头布这个简单的游戏中,我们可以有不同的策略。一种是完全随机地…

Oracle数据库文件变成32k故障恢复--惜分飞

最近一个客户数据库重启系统之后,数据文件大小变为了32kb,我接手的不是第一现场(客户那边尝试了rman还原操作),查看alert日志,数据库最初报错 Wed Jun 18 13:09:23 2025 alter database open Block change tracking file is current. Read of datafile D:\APP\ADMINISTRATOR\OR…

移动端 uniapp 写一个可自由拖拽的小键盘

写之前要考虑&#xff1a; 键盘展开后&#xff0c;不能超过手机边缘在底部展开键盘&#xff0c;键盘应出现在展开按钮上方&#xff1b;以此类推重复点击展开按钮&#xff0c;关闭键盘 效果&#xff1a; 代码如下&#xff0c;有些按键逻辑还需要优化 <template><vi…

《二分枚举答案(配合数据结构)》题集

文章目录 1、模板题集2、课内题集3、课后题集1. 字符串哈希2. 并查集3. ST表 1、模板题集 分巧克力 2、课内题集 倒水 冶炼金属 连续子序列的个数 3、课后题集 括号内的整数代表完整代码行数。 1. 字符串哈希 你猜猜是啥题(60) 2. 并查集 拯救萌萌(72) 3. ST表 GCD不小…

PY32F030单片机,优势替代ST GD,主频48MHz,带LED数码管驱动

PY32F030是一款高性能32位单片机&#xff0c;采用ARM Cortex-M0内核&#xff0c;工作频率高达48MHz&#xff0c;具备64KB Flash和8KB SRAM。它支持1.7V~5.5V宽电压范围&#xff0c;集成多路I2C、SPI、USART通讯外设&#xff0c;配备12位ADC、16位定时器和比较器&#xff0c;适用…