怎样将“QtWidgets.QLineEdit”置为空

本文介绍了如何在Python中使用Qt库的QLineEdit类创建一个文本输入框,并演示了如何清空其内容。
lineEdit = QtWidgets.QLineEdit()
lineEdit.clear()

修改代码,将代码中用到的所有IP 端口 用户名 密码通过data.ini 保存 并支持选择 #!/usr/bin/env python3 # -*- coding: utf-8__ """ 继电器 TCP 控制 + IP 可达性探测 + 远程关机校验 + 下位机指示灯 (连接按钮状态修复:默认灰→成功绿→再次点击恢复灰) """ import sys import time import socket import paramiko from threading import Thread from PySide6.QtWidgets import (QApplication, QWidget, QLineEdit, QPushButton, QHBoxLayout, QVBoxLayout, QMessageBox, QLabel) from PySide6.QtCore import QObject, Signal, Qt # ---------- 通用工具 ---------- def tcp_port_alive(ip: str, port: int, timeout: int = 2) -> bool: with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: s.settimeout(timeout) return s.connect_ex((ip, port)) == 0 def host_reachable(ip: str, port: int = 502) -> tuple[bool, str]: if tcp_port_alive(ip, port): return True, f'TCP {port} 端口开放' return False, '502 端口无法连接' def shutdown_remote_linux(remote_host: str, ssh_user: str, ssh_pass: str, port: int = 22) -> None: ssh = paramiko.SSHClient() ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) try: ssh.connect(remote_host, port=port, username=ssh_user, password=ssh_pass) stdin, stdout, stderr = ssh.exec_command('sudo shutdown -h now') stdout.channel.recv_exit_status() except Exception as e: raise RuntimeError(f"SSH 关机失败: {e}") from e finally: ssh.close() def verify_shutdown(remote_host: str, port: int = 22, timeout: int = 3) -> bool: try: with socket.create_connection((remote_host, port), timeout=timeout): return False except (socket.timeout, socket.error): return True def wait_shutdown_complete(remote_host: str, port: int = 22, max_wait: int = 60) -> None: for _ in range(max_wait): if verify_shutdown(remote_host, port): return time.sleep(1) raise RuntimeError("等待关机超时") def openlinux(remote_host: str, port: int = 22) -> bool: while True: try: with socket.create_connection((remote_host, port), timeout=5): return True except (socket.timeout, socket.error): continue # ---------- 探测线程 ---------- class DetectWorker(QObject): done = Signal(bool, str) ssh_status_first = Signal(bool, str) def __init__(self, ip: str, port: int, ssh_ip: str, ssh_port: int = 22): super().__init__() self.ip, self.port = ip, port self.ssh_ip, self._ssh_port = ssh_ip, ssh_port def run(self): ok, msg = host_reachable(self.ip, self.port) ssh_ok = tcp_port_alive(self.ssh_ip, self._ssh_port, timeout=2) if ssh_ok: self.ssh_status_first.emit(True, "下位机已开机") else: self.ssh_status_first.emit(False, "下位机未连接") self.done.emit(ok, msg) # ---------- 继电器 ---------- class RelayControl: def __init__(self): self.client_socket = None self._ip = None self._port = None def tcp_connect(self, ip_addr: str, ip_port: int): self._ip, self._port = ip_addr, ip_port self._reconnect() def _reconnect(self): try: if self.client_socket: self.client_socket.close() except Exception: pass self.client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.client_socket.settimeout(3) self.client_socket.connect((self._ip, self._port)) def _ensure_socket(self): try: self.client_socket.getpeername() except (OSError, AttributeError): self._reconnect() def tcp_disconnect(self): try: self.client_socket.close() except Exception: pass self.client_socket = None def power_up_1(self): self._ensure_socket() self.client_socket.sendall(bytes.fromhex('00000000000601052040FF00')) def power_up_2(self): self._ensure_socket() self.client_socket.sendall(bytes.fromhex('000100000006010520400000')) def power_down_1(self): self._ensure_socket() self.client_socket.sendall(bytes.fromhex('00020000000601052041FF00')) def power_down_2(self): self._ensure_socket() self.client_socket.sendall(bytes.fromhex('000300000006010520410000')) # ---------- 启动/停止线程(仅 PLC) ---------- class Worker(QObject): finished = Signal() error = Signal(str) def __init__(self, relay: RelayControl, up: bool): super().__init__() self.relay = relay self.up = up def run(self): try: if self.up: self.relay.power_up_1() time.sleep(1) self.relay.power_up_2() else: self.relay.power_down_1() time.sleep(1) self.relay.power_down_2() self.finished.emit() except Exception as e: self.error.emit(str(e)) # ---------- 独立关机线程(仅 SSH) ---------- class ShutdownWorker(QObject): finished = Signal() error = Signal(str) def __init__(self, ssh_ip: str, ssh_user: str, ssh_pwd: str): super().__init__() self.ssh_ip = ssh_ip self.ssh_user = ssh_user self.ssh_pwd = ssh_pwd def run(self): try: shutdown_remote_linux(self.ssh_ip, self.ssh_user, self.ssh_pwd) wait_shutdown_complete(self.ssh_ip) self.finished.emit() except Exception as e: self.error.emit(str(e)) # ---------- 指示灯线程 ---------- class IndicatorWorker(QObject): status_changed = Signal(bool, str) def __init__(self, target_ip: str, port: int = 22): super().__init__() self.target_ip = target_ip self.port = port self._running = True self._wait_for_on = False self._wait_for_off = False def wait_for_on(self): self._wait_for_on = True self._wait_for_off = False def wait_for_off(self): self._wait_for_off = True self._wait_for_on = False def stop(self): self._running = False def run(self): while self._running: if self._wait_for_on: if openlinux(self.target_ip, self.port): self.status_changed.emit(True, "下位机已开机") self._wait_for_on = False elif self._wait_for_off: if verify_shutdown(self.target_ip, self.port): self.status_changed.emit(False, "下位机已关机") self._wait_for_off = False else: time.sleep(0.2) # ---------- GUI ---------- class MainWindow(QWidget): def __init__(self): super().__init__() # ════════════ 可调常量 ════════════ WIN_WIDTH = 490 # 窗口宽 WIN_HEIGHT = 260 # 窗口高 IP_WIDTH = 110 # IP输入框统一宽 PORT_WIDTH = 80 # 端口框宽 USER_WIDTH = 80 # 用户名框宽 BTN_WIDTH = 80 # 按钮统一宽 FONT_SIZE = 13 # 字体大小 BG_COLOR = "#f5f5f5" # 背景色 BTN_COLOR = "#e5e5e5" # 按钮常态色 BTN_HOVER = "#d0d0d0" # 按钮悬停色 BTN_PRESSED = "#c0c0c0" # 按钮按下色 # ═════════════════════════════════ self.setFixedSize(WIN_WIDTH, WIN_HEIGHT) # 固定窗口 self.setWindowTitle('HIL 开关机控制') self.setStyleSheet(f""" QWidget{{ background-color:{BG_COLOR}; font:{FONT_SIZE}px "Microsoft YaHei"; }} QLineEdit{{ height:24px; border:1px solid #ccc; border-radius:4px; padding:0 4px; background:white; }} QPushButton{{ height:26px; border:1px solid #bbb; border-radius:4px; background:{BTN_COLOR}; min-width:{BTN_WIDTH}px; }} QPushButton:hover{{ background:{BTN_HOVER}; }} QPushButton:pressed{{ background:{BTN_PRESSED}; }} QLabel{{ color:#333; }} """) # ---------------- 创建控件 ---------------- self.ip_edit = QLineEdit('192.168.1.3') self.port_edit = QLineEdit('502') self.port_edit.setInputMask('00000') self.ssh_ip_edit = QLineEdit('192.168.1.119') self.ssh_user_edit = QLineEdit('root') self.ssh_pwd_edit = QLineEdit('aertp2020') self.ssh_pwd_edit.setEchoMode(QLineEdit.Password) # 统一宽度 self.ip_edit.setFixedWidth(IP_WIDTH) self.ssh_ip_edit.setFixedWidth(IP_WIDTH) self.port_edit.setFixedWidth(PORT_WIDTH) self.ssh_user_edit.setFixedWidth(USER_WIDTH) self.conn_btn = QPushButton('连接') self.conn_btn.setCheckable(True) # 允许点击切换状态 self.conn_btn.setFixedWidth(BTN_WIDTH) self.conn_btn.clicked.connect(self.on_toggle_connect) self._set_conn_style(False) # 初始灰色 self.start_btn = QPushButton('启动') self.stop_btn = QPushButton('停止') self.shutdown_btn = QPushButton('关闭Linux') self.start_btn.setFixedWidth(BTN_WIDTH) self.stop_btn.setFixedWidth(BTN_WIDTH) self.shutdown_btn.setFixedWidth(BTN_WIDTH) self.start_btn.clicked.connect(lambda: self.on_ctrl(up=True)) self.stop_btn.clicked.connect(lambda: self.on_ctrl(up=False)) self.shutdown_btn.clicked.connect(self.on_shutdown) self.indicator = QLabel('●') self.indicator.setFixedWidth(31) self.indicator.setStyleSheet("color: gray; font-size: 32px;") self.indicator_text = QLabel("下位机未连接") # ---------------- 布局 ---------------- row1 = QHBoxLayout() row1.addWidget(QLabel('继电器IP:')) row1.addWidget(self.ip_edit) row1.addWidget(QLabel('端 口:')) row1.addWidget(self.port_edit) row1.addWidget(self.conn_btn) row1.addStretch() row2 = QHBoxLayout() row2.addWidget(QLabel('下位机IP:')) row2.addWidget(self.ssh_ip_edit) row2.addWidget(QLabel('用户名:')) row2.addWidget(self.ssh_user_edit) row2.addWidget(QLabel('密码:')) row2.addWidget(self.ssh_pwd_edit) row2.addStretch() row3 = QHBoxLayout() row3.addWidget(self.start_btn) row3.addWidget(self.stop_btn) row3.addWidget(self.shutdown_btn) row3.addWidget(QLabel('下位机状态:')) row3.addWidget(self.indicator) row3.addWidget(self.indicator_text) row3.addStretch() main = QVBoxLayout(self) main.addLayout(row1) main.addLayout(row2) main.addLayout(row3) main.addStretch() # 业务对象 self.relay = RelayControl() self._thread = None self.indicator_thread = None self.indicator_worker = None self._ssh_ever_connected = False self._set_ctrl_enabled(False) # ---------------- 连接/断开 ---------------- def on_toggle_connect(self): if self.conn_btn.isChecked(): # 要连接 ip = self.ip_edit.text().strip() if not ip: QMessageBox.warning(self, '提示', 'IP 地址不能为') self.conn_btn.setChecked(False) return try: port = int(self.port_edit.text()) except ValueError: QMessageBox.warning(self, '提示', '端口必须是数字') self.conn_btn.setChecked(False) return self.conn_btn.setEnabled(False) self.conn_btn.setText('连接中…') self.detector = DetectWorker(ip, port, self.ssh_ip_edit.text().strip()) self.detector.ssh_status_first.connect(self._set_indicator) self.detector.done.connect(self._on_detect_done) Thread(target=self.detector.run, daemon=True).start() else: # 要断开 try: self.relay.tcp_disconnect() except Exception: pass self._set_conn_style(False) # 恢复灰色 self._set_ctrl_enabled(False) self._stop_indicator() self._set_indicator(False, "下位机未连接") def _on_detect_done(self, ok: bool, msg: str): self.conn_btn.setEnabled(True) if not ok: QMessageBox.warning(self, '不可达', f'{self.ip_edit.text().strip()} 不可达({msg})') self.conn_btn.setChecked(False) self._set_conn_style(False) return try: self.relay.tcp_connect(self.ip_edit.text().strip(), int(self.port_edit.text())) self._set_conn_style(True) self._set_ctrl_enabled(True) self._start_indicator() except Exception as e: QMessageBox.critical(self, '错误', f'连接失败:\n{e}') self.conn_btn.setChecked(False) self._set_conn_style(False) # ---------------- 启动/停止(仅PLC) ---------------- def on_ctrl(self, up: bool): self.start_btn.setEnabled(False) self.stop_btn.setEnabled(False) self.shutdown_btn.setEnabled(False) if up: self.indicator_worker.wait_for_on() self.worker = Worker(self.relay, up=up) self.worker.finished.connect(self._done) self.worker.error.connect(self._error) self._thread = Thread(target=self.worker.run, daemon=True) self._thread.start() # ---------------- 独立关闭Linux(仅SSH) ---------------- def on_shutdown(self): self.start_btn.setEnabled(False) self.stop_btn.setEnabled(False) self.shutdown_btn.setEnabled(False) self.indicator_worker.wait_for_off() self.shutdown_worker = ShutdownWorker( self.ssh_ip_edit.text().strip(), self.ssh_user_edit.text().strip(), self.ssh_pwd_edit.text().strip()) self.shutdown_worker.finished.connect(self._shutdown_done) self.shutdown_worker.error.connect(self._shutdown_error) Thread(target=self.shutdown_worker.run, daemon=True).start() # ---------------- 指示灯(仅灰关闭按钮) ---------------- def _start_indicator(self): self.indicator_worker = IndicatorWorker(self.ssh_ip_edit.text().strip()) self.indicator_worker.status_changed.connect(self._set_indicator) self.indicator_thread = Thread(target=self.indicator_worker.run, daemon=True) self.indicator_thread.start() def _stop_indicator(self): if self.indicator_worker: self.indicator_worker.stop() self.indicator_worker = None def _set_indicator(self, on: bool, text: str = ""): color = "green" if on else "gray" self.indicator.setStyleSheet(f"color: {color}; font-size: 32px;") if text: self.indicator_text.setText(text) # 仅灰关闭按钮:曾连通就永久启用 if on and not self._ssh_ever_connected: self._ssh_ever_connected = True self.shutdown_btn.setEnabled(True) # ---------------- 回调 ---------------- def _done(self): self.start_btn.setEnabled(True) self.stop_btn.setEnabled(True) self.shutdown_btn.setEnabled(True) QMessageBox.information(self, '成功', '指令执行完成') def _error(self, msg): self.start_btn.setEnabled(True) self.stop_btn.setEnabled(True) self.shutdown_btn.setEnabled(True) QMessageBox.critical(self, '错误', f'指令执行失败:\n{msg}') def _shutdown_done(self): self.start_btn.setEnabled(True) self.stop_btn.setEnabled(True) self.shutdown_btn.setEnabled(True) QMessageBox.information(self, '成功', '下位机已关闭') def _shutdown_error(self, msg): self.start_btn.setEnabled(True) self.stop_btn.setEnabled(True) self.shutdown_btn.setEnabled(True) QMessageBox.critical(self, '错误', f'关闭下位机失败:\n{msg}') # ---------------- UI 辅助 ---------------- def _set_conn_style(self, connected: bool): if connected: self.conn_btn.setText('已连接') self.conn_btn.setStyleSheet('background-color:#4CAF50;color:white;') else: self.conn_btn.setText('连接') self.conn_btn.setStyleSheet('background-color:#e5e5e5;color:black;') # 恢复系统灰色 def _set_ctrl_enabled(self, on: bool): self.start_btn.setEnabled(on) self.stop_btn.setEnabled(on) # 仅当 PLC 已连接且 SSH 曾通才允许关机 self.shutdown_btn.setEnabled(on and self._ssh_ever_connected) # -------------------- main -------------------- if __name__ == '__main__': app = QApplication(sys.argv) w = MainWindow() w.show() sys.exit(app.exec())
最新发布
11-26
import sys import json import os import warnings from datetime import datetime from PyQt5.QtCore import QUrl, Qt, QDateTime from PyQt5.QtWidgets import ( QApplication, QMainWindow, QLineEdit, QPushButton, QListWidget, QTabWidget, QVBoxLayout, QWidget, QHBoxLayout, QAction, QToolBar, QStatusBar, QProgressBar, QInputDialog, QFileDialog, QMessageBox ) from PyQt5.QtWebEngineWidgets import QWebEngineView, QWebEngineProfile, QWebEnginePage, QWebEngineSettings from PyQt5.QtNetwork import QNetworkProxy, QNetworkCookie # 修复代理设置导入问题 # 忽略弃用警告 warnings.filterwarnings("ignore", category=DeprecationWarning) class WebEnginePage(QWebEnginePage): def __init__(self, profile=None, parent=None): """修复参数传递问题:支持带profile和不带profile两种初始化方式""" if profile: super().__init__(profile, parent) # 使用profile初始化 else: super().__init__(parent) # 标准初始化 self.parent_window = parent.parent_window if parent and hasattr(parent, 'parent_window') else None def acceptNavigationRequest(self, url, _type, isMainFrame): # 检测视频流协议并调用播放器 if url.scheme() in ['rtmp', 'http-flv', 'ws-flv']: if self.parent_window: self.parent_window.play_with_jessibuca(url.toString()) return False return super().acceptNavigationRequest(url, _type, isMainFrame) def createWindow(self, type): # 在新标签页打开链接 if type == QWebEnginePage.WebBrowserTab: if self.parent_window: new_tab = self.parent_window.add_browser_tab("加载中...") return new_tab.page return super().createWindow(type) def javaScriptConsoleMessage(self, level, message, lineNumber, sourceID): """修复:正确映射JavaScript控制台日志级别""" # 创建日志级别映射字典 level_map = { QWebEnginePage.InfoMessageLevel: "INFO", QWebEnginePage.WarningMessageLevel: "WARNING", QWebEnginePage.ErrorMessageLevel: "ERROR" } # 获取可读的日志级别名称 level_str = level_map.get(level, "UNKNOWN") log_msg = f"[JS {level_str}] {sourceID}:{lineNumber} - {message}" if self.parent_window: self.parent_window.status_bar.showMessage(log_msg, 5000) print(log_msg) # 同时输出到控制台 class WebEngineView(QWebEngineView): def __init__(self, parent=None): super().__init__(parent) self.parent_window = parent # 修复:正确传递profile参数 self.page = WebEnginePage(profile=self.parent_window.profile, parent=self) self.setPage(self.page) self.page.loadFinished.connect(self.inject_jessibuca) def inject_jessibuca(self): self.page.runJavaScript(""" if (typeof window.Jessibuca === 'undefined') { const script = document.createElement('script'); script.src = 'https://cdn.jsdelivr.net/npm/jessibuca@latest/dist/jessibuca.js'; script.onerror = () => console.error('Jessibuca加载失败'); document.head.appendChild(script); const container = document.createElement('div'); container.id = 'jessibuca-container'; container.style.position = 'fixed'; container.style.zIndex = '99999'; container.style.top = '0'; container.style.left = '0'; container.style.width = '100%'; container.style.height = '100%'; container.style.backgroundColor = 'black'; container.style.display = 'none'; document.body.appendChild(container); } """) def createWindow(self, type): # 创建新标签页 if type == QWebEnginePage.WebBrowserTab: new_tab = WebEngineView(self.parent_window) # 修复:直接返回page属性而非调用page() return new_tab.page return super().createWindow(type) class MainWindow(QMainWindow): def __init__(self): super().__init__() self.setWindowTitle("B站多账号浏览器") self.setMinimumSize(1200, 800) # 初始化数据存储 self.account_db = {} self.cookies_db = [] # 创建持久化Cookie配 self.cookie_storage_path = "cookies_storage" os.makedirs(self.cookie_storage_path, exist_ok=True) self.profile = QWebEngineProfile("BiliCookieProfile", self) self.profile.setPersistentCookiesPolicy(QWebEngineProfile.ForcePersistentCookies) self.profile.setPersistentStoragePath(self.cookie_storage_path) # 关键性能优化1: 全局禁用网络代理[8](@ref) QNetworkProxy.setApplicationProxy(QNetworkProxy(QNetworkProxy.NoProxy)) # 关键性能优化2: 启用HTTP内存缓存[9](@ref) self.profile.setHttpCacheType(QWebEngineProfile.MemoryHttpCache) # 启用HTML5支持的设置 profile_settings = self.profile.settings() profile_settings.setAttribute(QWebEngineSettings.PlaybackRequiresUserGesture, False) profile_settings.setAttribute(QWebEngineSettings.FullScreenSupportEnabled, True) profile_settings.setAttribute(QWebEngineSettings.WebGLEnabled, True) profile_settings.setAttribute(QWebEngineSettings.Accelerated2dCanvasEnabled, True) profile_settings.setAttribute(QWebEngineSettings.PluginsEnabled, True) profile_settings.setAttribute(QWebEngineSettings.AllowRunningInsecureContent, True) # 主界面布局 central_widget = QWidget() self.setCentralWidget(central_widget) main_layout = QVBoxLayout(central_widget) # 导航工具栏 navbar = QToolBar("导航栏") navbar.setMovable(False) # 禁止工具栏拖动提升性能 self.addToolBar(navbar) back_btn = QAction("←", self) back_btn.triggered.connect(self.navigate_back) navbar.addAction(back_btn) forward_btn = QAction("→", self) forward_btn.triggered.connect(self.navigate_forward) navbar.addAction(forward_btn) reload_btn = QAction("↻", self) reload_btn.triggered.connect(self.reload_page) navbar.addAction(reload_btn) home_btn = QAction("🏠", self) home_btn.triggered.connect(self.navigate_home) navbar.addAction(home_btn) navbar.addSeparator() self.url_bar = QLineEdit("https://www.bilibili.com") self.url_bar.setPlaceholderText("输入网址或搜索内容...") self.url_bar.returnPressed.connect(self.load_url) navbar.addWidget(self.url_bar) # 浏览器标签页 self.browser_tabs = QTabWidget() self.browser_tabs.setTabsClosable(True) self.browser_tabs.tabCloseRequested.connect(self.close_tab) self.browser_tabs.currentChanged.connect(self.update_url_bar) # 关键性能优化3: 启用标签页滚动 self.browser_tabs.setUsesScrollButtons(True) main_layout.addWidget(self.browser_tabs, 8) # 添加初始标签页 self.add_browser_tab("首页", "https://www.bilibili.com") # 管理面板 self.tabs = QTabWidget() self.cookie_list = QListWidget() self.tabs.addTab(self.cookie_list, "Cookie列表") self.account_list = QListWidget() self.tabs.addTab(self.account_list, "账号切换") self.account_list.itemClicked.connect(self.switch_account) main_layout.addWidget(self.tabs, 2) # 功能按钮 btn_layout = QHBoxLayout() self.import_btn = QPushButton("导入Cookie") self.import_btn.clicked.connect(self.import_cookies) self.save_btn = QPushButton("保存账号") self.save_btn.clicked.connect(self.save_account) self.export_btn = QPushButton("导出Cookie") self.export_btn.clicked.connect(self.export_cookies) self.new_tab_btn = QPushButton("新建标签页") self.new_tab_btn.clicked.connect(self.create_new_tab) for btn in [self.import_btn, self.save_btn, self.export_btn, self.new_tab_btn]: btn_layout.addWidget(btn) main_layout.addLayout(btn_layout) # 状态栏 self.status_bar = QStatusBar() self.setStatusBar(self.status_bar) self.progress_bar = QProgressBar() self.progress_bar.setMaximum(100) self.progress_bar.setVisible(False) self.progress_bar.setFixedWidth(200) self.status_bar.addPermanentWidget(self.progress_bar) # 加载数据 self.load_accounts() def add_browser_tab(self, title, url=None): browser = WebEngineView(self) browser.titleChanged.connect(lambda title, b=browser: self.update_tab_title(b, title)) browser.loadProgress.connect(self.update_progress) browser.loadFinished.connect(self.on_load_finished) index = self.browser_tabs.addTab(browser, title) self.browser_tabs.setCurrentIndex(index) # 关键性能优化4: 延迟加载提升启动速度 if url: QApplication.processEvents() # 确保UI更新 browser.load(QUrl(url)) return browser def update_tab_title(self, browser, title): index = self.browser_tabs.indexOf(browser) if index != -1: self.browser_tabs.setTabText(index, title[:15] + "..." if len(title) > 15 else title) def update_progress(self, progress): self.progress_bar.setVisible(progress < 100) self.progress_bar.setValue(progress) def close_tab(self, index): """修复资源释放问题:移除多余的括号""" if self.browser_tabs.count() > 1: widget = self.browser_tabs.widget(index) # 关键修复:直接访问page属性而非调用page() page = widget.page if page: # 关键优化: 正确的资源释放顺序 profile = page.profile() profile.cookieStore().deleteAllCookies() # 先删除页面再删除视图 page.deleteLater() widget.deleteLater() self.browser_tabs.removeTab(index) else: self.create_new_tab() def create_new_tab(self): self.add_browser_tab("新标签页", "about:blank") def navigate_back(self): current_browser = self.browser_tabs.currentWidget() if current_browser: current_browser.back() def navigate_forward(self): current_browser = self.browser_tabs.currentWidget() if current_browser: current_browser.forward() def reload_page(self): current_browser = self.browser_tabs.currentWidget() if current_browser: current_browser.reload() def navigate_home(self): self.load_url("https://www.bilibili.com") def on_load_finished(self, success): browser = self.sender() if browser and self.browser_tabs.currentWidget() == browser: current_url = browser.url().toString() self.url_bar.setText(current_url) self.status_bar.showMessage("页面加载完成" if success else "页面加载失败", 2000) # 关键优化: 登录页面自动处理 if "login" in current_url or "passport" in current_url: self.status_bar.showMessage("检测到登录页面,请完成登录", 5000) self.auto_handle_login_page(browser) def auto_handle_login_page(self, browser): """自动处理登录页面的JavaScript""" browser.page.runJavaScript(""" // 尝试自动填充已知的登录表单 const loginForm = document.querySelector('form[action*="login"]'); if (loginForm) { // 尝试填充测试账号 const usernameInput = loginForm.querySelector('input[name="username"], input[name="user"]'); const passwordInput = loginForm.querySelector('input[name="password"]'); if (usernameInput && passwordInput) { usernameInput.value = "test_account"; passwordInput.value = "test_password"; console.log("自动填充了登录表单"); } } """) def update_url_bar(self): current_browser = self.browser_tabs.currentWidget() if current_browser: current_url = current_browser.url().toString() self.url_bar.setText(current_url) # 登录页面特殊处理 if "login" in current_url or "passport" in current_url: self.url_bar.setStyleSheet("background-color: #FFF8E1;") else: self.url_bar.setStyleSheet("") def load_url(self, url_text=None): if url_text is None: url_text = self.url_bar.text().strip() if not url_text: return # 关键优化: 更智能的URL处理[10](@ref) if not url_text.startswith(("http://", "https://", "file://", "ftp://")): if "." in url_text: # 包含域名 url_text = "https://" + url_text else: # 可能是搜索内容 url_text = f"https://www.bilibili.com/search?keyword={url_text}" current_browser = self.browser_tabs.currentWidget() if current_browser: current_browser.load(QUrl(url_text)) self.status_bar.showMessage(f"正在加载: {url_text}", 3000) def play_with_jessibuca(self, stream_url): """使用Jessibuca播放视频流""" current_browser = self.browser_tabs.currentWidget() if not current_browser: return self.status_bar.showMessage("Jessibuca播放中...", 3000) current_browser.page.runJavaScript(f""" const container = document.getElementById('jessibuca-container'); if (container) {{ container.style.display = 'block'; if (!window.jessibucaPlayer) {{ window.jessibucaPlayer = new Jessibuca({{ container: container, videoBuffer: 0.2, isResize: true, text: '直播加载中...', decoder: 'ffmpeg.js', forceNoOffscreen: true }}); }} window.jessibucaPlayer.play('{stream_url}'); }} """) def import_cookies(self): """导入Cookie JSON文件""" file_path, _ = QFileDialog.getOpenFileName( self, "选择Cookie文件", "", "JSON文件 (*.json)" ) if file_path: try: with open(file_path, "r", encoding='utf-8') as f: cookies = json.load(f) if isinstance(cookies, list): self.cookies_db = cookies self.cookie_list.clear() self.cookie_list.addItems([f"{c['name']}: {c['value'][:10]}..." for c in cookies]) self.status_bar.showMessage(f"成功导入 {len(cookies)} 个Cookie", 3000) self.inject_cookies(cookies) else: QMessageBox.warning(self, "错误", "无效的Cookie格式") except Exception as e: QMessageBox.critical(self, "导入失败", f"错误: {str(e)}") def export_cookies(self): """导出Cookie到文件""" file_path, _ = QFileDialog.getSaveFileName( self, "保存Cookie文件", "", "JSON文件 (*.json)" ) if file_path: try: cookies = self.get_current_cookies() with open(file_path, "w", encoding='utf-8') as f: json.dump(cookies, f, indent=2, ensure_ascii=False) self.status_bar.showMessage("Cookie导出成功", 3000) except Exception as e: QMessageBox.critical(self, "导出失败", f"错误: {str(e)}") def get_current_cookies(self): """获取当前标签页的Cookie(模拟)""" # 实际实现需要异步获取,这里简化处理 return self.cookies_db if self.cookies_db else [] def inject_cookies(self, cookies): """将Cookie注入当前页面 - 修复登录问题[9](@ref)""" current_browser = self.browser_tabs.currentWidget() if current_browser: store = current_browser.page.profile().cookieStore() # 先清除现有Cookie store.deleteAllCookies() # 异步注入新的Cookie for cookie_data in cookies: # 创建Qt的Cookie对象 qt_cookie = QNetworkCookie( cookie_data['name'].encode('utf-8'), cookie_data['value'].encode('utf-8') ) # 设置Cookie属性 if 'domain' in cookie_data: qt_cookie.setDomain(cookie_data['domain']) if 'path' in cookie_data: qt_cookie.setPath(cookie_data['path']) if 'expiry' in cookie_data: # 转换为QDateTime expiry = QDateTime.fromSecsSinceEpoch(cookie_data['expiry']) qt_cookie.setExpirationDate(expiry) # 设置安全属性 qt_cookie.setSecure(cookie_data.get('secure', False)) qt_cookie.setHttpOnly(cookie_data.get('httpOnly', False)) # 注入Cookie store.setCookie(qt_cookie, QUrl(cookie_data.get('url', 'https://www.bilibili.com'))) self.status_bar.showMessage("Cookie注入成功,请刷新页面", 3000) def save_account(self): """保存当前账号配""" account_name, ok = QInputDialog.getText( self, "保存账号", "输入账号名称:" ) if ok and account_name: cookies = self.get_current_cookies() if cookies: self.account_db[account_name] = { "cookies": cookies, "saved_date": datetime.now().isoformat() } self.account_list.addItem(account_name) # 持久化存储账号数据 try: with open("accounts.json", "w", encoding='utf-8') as f: json.dump(self.account_db, f, indent=2, ensure_ascii=False) QMessageBox.information(self, "成功", "账号保存成功") except Exception as e: QMessageBox.critical(self, "保存失败", f"账号保存失败: {str(e)}") else: QMessageBox.warning(self, "错误", "没有获取到有效Cookie") def switch_account(self, item): """切换账号""" account_name = item.text() if account_name in self.account_db: cookies = self.account_db[account_name].get("cookies", []) self.inject_cookies(cookies) self.status_bar.showMessage(f"已切换至账号: {account_name}", 3000) # 自动刷新当前页面 self.reload_page() else: QMessageBox.warning(self, "错误", "找不到该账号的Cookie信息") def load_accounts(self): """从文件加载保存的账号""" try: if os.path.exists("accounts.json"): with open("accounts.json", "r", encoding='utf-8') as f: self.account_db = json.load(f) self.account_list.addItems(self.account_db.keys()) except Exception as e: print(f"加载账号失败: {str(e)}") def closeEvent(self, event): """窗口关闭时释放全局资源[8](@ref)""" # 清理HTTP缓存和访问记录 self.profile.clearHttpCache() self.profile.clearAllVisitedLinks() # 保存账号数据 try: with open("accounts.json", "w", encoding='utf-8') as f: json.dump(self.account_db, f, indent=2, ensure_ascii=False) except Exception as e: print(f"保存账号失败: {str(e)}") super().closeEvent(event) if __name__ == "__main__": # 关键性能优化: 启用WebEngine调试日志[9](@ref) os.environ["QTWEBENGINE_CHROMIUM_FLAGS"] = "--enable-logging" app = QApplication(sys.argv) # 关键性能优化: 启用Qt内的OpenGL渲染[7](@ref) app.setAttribute(Qt.AA_UseOpenGLES) window = MainWindow() window.show() sys.exit(app.exec_())
07-09
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值