pyqtwebengine=5.12
PyQt5==5.12
class MyWindow(QMainWindow):
def __init__(self):
super(MyWindow, self).__init__()
self.browser = QWebEngineView(self) # 如果不写self则新生成一个窗口
self.browser.setWindowTitle('技术领域占比分析')
self.browser.setWindowIcon(QIcon(LOGO_PATH))
self.browser.raise_()
self.browser.setFixedSize(QSize(1200, 850))
self.browser.move(0, 0)
self.browser.setUrl(QUrl('https://www.taobao.com')) # 网络连接
# self.browser.load(QUrl.fromLocalFile('D:/1.htmml')) # 本地h5
self.browser.show()
if __name__ == '__main__':
app = QApplication(sys.argv)
window = MyWindow()
window.show()
sys.exit(app.exec_())
webview套壳 防多开 多次点击激活旧窗口
文件结构如下:
依赖
loguru==0.6.0
pyinstaller
pywin32==306
PyQt5==5.12
pyqtwebengine==5.12
Pillow
config.ini
[Section1]
open_loadfile = 1
window_title = 鸿运兑奖系统
window_width = 1580
window_height = 800
prohibit_max_window = 0
window_max = 0
open_url = https://www.taobao.com
min_window_width = 1300
min_window_height = 700
settings.py
import os
BASE_DIR = os.path.dirname(__file__)
STATIC_FILE = os.path.join(BASE_DIR, 'static') # 静态资源路径
INDEX_FILE = os.path.join(STATIC_FILE, 'index.html')
CONFIG_PATH = os.path.join(BASE_DIR, 'config') # 图标文件
LOGO_PATH = os.path.join(CONFIG_PATH, "logo.png") # 图标路径
INI_PATH = os.path.join(CONFIG_PATH, "config.ini") # 配置文件
tools.py
import configparser
class MyINIFile:
def __init__(self, filename):
self.filename = filename
self.config = configparser.ConfigParser()
self.config.read(self.filename)
def read_value(self, section, key):
try:
return self.config.get(section, key)
except configparser.Error as e:
print(f"Error reading value: {e}")
return None
def write_value(self, section, key, value):
try:
if not self.config.has_section(section):
self.config.add_section(section)
self.config.set(section, key, value)
with open(self.filename, 'w') as configfile:
self.config.write(configfile)
except configparser.Error as e:
print(f"Error writing value: {e}")
def delete_value(self, section, key):
try:
if self.config.has_section(section) and self.config.has_option(section, key):
self.config.remove_option(section, key)
with open(self.filename, 'w') as configfile:
self.config.write(configfile)
else:
print(f"Section '{section}' or option '{key}' does not exist.")
except configparser.Error as e:
print(f"Error deleting value: {e}")
def update_value(self, section, key, value):
try:
if self.config.has_section(section) and self.config.has_option(section, key):
self.config.set(section, key, value)
with open(self.filename, 'w') as configfile:
self.config.write(configfile)
else:
print(f"Section '{section}' or option '{key}' does not exist.")
except configparser.Error as e:
print(f"Error updating value: {e}")
ui.py
from PyQt5.QtCore import Qt, QUrl
from PyQt5.QtGui import QIcon
from PyQt5.QtWebEngineWidgets import QWebEngineView
from PyQt5.QtWidgets import QMainWindow, QSystemTrayIcon, QMenu, QAction, \
QApplication
from settings import LOGO_PATH, INDEX_FILE, INI_PATH
from tools import MyINIFile
class MyWindow(QMainWindow):
def __init__(self):
config = MyINIFile(INI_PATH)
self.open_loadfile = config.read_value('Section1', 'open_loadfile')
self._window_title = config.read_value('Section1', 'window_title') # 窗口标题
self._window_width = int(config.read_value('Section1', 'window_width'))# 窗口默认宽高
self._window_height = int(config.read_value('Section1', 'window_height')) # 窗口默认宽高
self.prohibit_max_window = config.read_value('Section1', 'prohibit_max_window') # 是否禁止最大化
self.window_max = config.read_value('Section1', 'window_max') # 是否默认最大化显示
self.open_url = config.read_value('Section1', 'open_url') # 网络链接
self._min_window_width = int(config.read_value('Section1', 'min_window_width')) # 最小窗口
self._min_window_height = int(config.read_value('Section1', 'min_window_height'))
super(MyWindow, self).__init__()
self.setWindowTitle(self._window_title) # 设置窗口标题
self.setMinimumSize(self._min_window_width, self._min_window_height)
self.resize(self._window_width, self._window_height)
self.setWindowIcon(QIcon(LOGO_PATH)) # 设置窗口图标,icon.png 是您的图标文件路径
if self.prohibit_max_window == '1':
self.setWindowFlags(self.windowFlags() & ~Qt.WindowMaximizeButtonHint) # 禁止最大化按钮
if self.window_max == '1':
self.showMaximized() # 最大化窗口
self.browser = QWebEngineView(self) # 如果不写self则新生成一个窗口
if self.open_loadfile == '1':
self.browser.load(QUrl.fromLocalFile(INDEX_FILE)) # 本地h5
else:
self.browser.setUrl(QUrl(self.open_url)) #
self.browser.show()
def resizeEvent(self, event):
# 设置 WebEngineView 的大小
self.browser.resize(self.size()) # 使用窗口的大小
super().resizeEvent(event)
def tray(self):
'''系统托盘'''
# 创建系统托盘图标
self.tray_icon = QSystemTrayIcon(self)
self.tray_icon.setIcon(QIcon(LOGO_PATH)) # 替换为你的图标路径
self.tray_icon.setToolTip(self._window_title) # 这里设置鼠标悬浮时显示的文字
self.tray_menu = QMenu() # 创建右键菜单
open_window_action = QAction("打开主界面", self) # 添加退出动作
exit_action = QAction("退出系统", self) # 添加退出动作
self.tray_menu.addAction(open_window_action) # 添加到菜单
self.tray_menu.addAction(exit_action)
open_window_action.triggered.connect(self.open_window_action)
exit_action.triggered.connect(self.exit_app) # 连接托盘图标的点击事件
self.tray_icon.activated.connect(self.tray_icon_clicked) # 图标左键被点击
self.tray_icon.setContextMenu(self.tray_menu) # 将菜单设置到托盘图标
self.tray_icon.show() # 显示托盘图标
def tray_icon_clicked(self, reason):
if reason == QSystemTrayIcon.Trigger: # 单击托盘图标
if self.isHidden():
self.show() # 显示窗口
if self.isMinimized():
self.showNormal() # 还原窗口
self.activateWindow() # 激活窗口
self.raise_() # 确保窗口在最上层
def open_window_action(self):
'''打开主界面'''
if self.isHidden():
self.show() # 显示窗口
if self.isMinimized():
self.showNormal() # 还原窗口
self.activateWindow() # 激活窗口
self.raise_() # 确保窗口在最上层
def exit_app(self):
QApplication.quit() # 退出应用
def closeEvent(self, event):
'''窗口被关闭事件'''
event.ignore()
self.hide()
main.py
import socket
import sys
from threading import Thread
from PyQt5.QtCore import QObject, pyqtSignal
from PyQt5.QtWidgets import QApplication
from loguru import logger
from ui import MyWindow
class Communicator(QObject):
activate_signal = pyqtSignal()
def check_if_running(port):
"""检查端口是否被占用,意味着应用正在运行"""
with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as s:
try:
s.bind(('127.0.0.1', port))
return False # 绑定成功,表示没有其他实例在运行
except OSError:
return True # 绑定失败,表示端口被占用
def activate_existing_instance(port):
"""激活已运行的实例"""
with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as s:
s.sendto(b'activate', ('127.0.0.1', port))
def start_server(port, comm):
"""启动服务器以监听激活请求"""
with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as s:
s.bind(('127.0.0.1', port))
logger.debug('启动成功')
while True:
data, addr = s.recvfrom(1024)
if data == b'activate':
logger.info("激活请求收到, 启动窗口")
comm.activate_signal.emit() # 发射信号以激活窗口
if __name__ == '__main__':
app = QApplication(sys.argv)
port = 12345 # 选择一个未被占用的端口
comm = Communicator()
main_window = MyWindow()
comm.activate_signal.connect(main_window.open_window_action)
if check_if_running(port):
activate_existing_instance(port)
logger.error("应用已经在运行,激活窗口。")
sys.exit(0)
# 启动服务器监听激活请求
server_thread = Thread(target=start_server, args=(port, comm))
server_thread.daemon = True
server_thread.start()
main_window.tray()
# 创建并显示主窗口
main_window.show()
sys.exit(app.exec_())
pack.py
# coding=utf-8
# @Time : 2023-08-17 02:04
# @Author : XiaoYi
# @Email: 1206154726@qq.com
import os
import shutil
from loguru import logger
desktop_dist = os.path.join(os.path.join(os.path.expanduser("~"), 'Desktop'), 'dist')
desktop_main = os.path.join(desktop_dist, 'main')
# from urllib.parse import quote as url_quote py3.8以上
logger.debug(f'----------------------------开始构建程序--------------------------')
os.system('pyinstaller -w main.py -i logo.ico') # 执行打包指令 pyinstaller -w main.py -i logo.ico 无调试窗口
logger.success(f'------->基础镜像结束,开始打包静态资源')
shutil.move(os.path.join(os.getcwd(), 'dist'), desktop_dist) # 移动文件到桌面
logger.debug(f'------->【dist】基础镜像结束')
shutil.rmtree(os.path.join(os.getcwd(), 'build')) # 删除打包构建文件
os.remove(os.path.join(os.getcwd(), 'main.spec')) # 删除打包构建文件
logger.warning(f'------->【编译文件清除】清除结束')
shutil.copytree(os.path.join(os.getcwd(), 'config'), os.path.join(desktop_main, 'config'))
shutil.copytree(os.path.join(os.getcwd(), 'static'), os.path.join(desktop_main, 'static'))
os.rename(os.path.join(desktop_main, 'main.exe'), os.path.join(desktop_main, 'taoke.exe')) # 重命名打包后的文件
os.rename(desktop_main, os.path.join(desktop_dist, 'taoke')) # 重命名打包后的文件
logger.success(f'-------【打包结束】----------')