Windows下如何禁止用户退出软件回到桌面
软件禁止用户退出到桌面
开发背景
在快递柜、银行办事大厅自助柜等公共场所的自助终端中,禁止用户退出到桌面的核心思路是构建一个高度锁定的操作环境,通过系统级限制、应用程序设计和硬件辅助等多重手段实现。以下是具体实现方式:
系统级锁定(最核心手段)
(1)定制化操作系统或嵌入式系统
专用精简系统:不使用普通 Windows/macOS,而是采用嵌入式系统(如 Linux 发行版、Android 定制版),移除所有不必要的系统功能(如桌面、任务栏、快捷键)。
(2)kiosk 模式(自助服务模式):
在 Windows 中可通过组策略或注册表启用 “kiosk 模式”,仅允许指定程序运行,禁用所有系统级操作(如Win键、Ctrl+Alt+Del、任务管理器)。
示例:Windows 的 “分配的访问” 功能可将系统锁定到单个应用,用户无法退出。
(3)屏蔽系统快捷键和操作
拦截核心快捷键:通过系统钩子(如 Windows 的SetWindowsHookEx)屏蔽Alt+Tab、Win+D(显示桌面)、Ctrl+Shift+Esc(任务管理器)、Alt+F4等关键组合键。
禁用右键菜单:在系统层面屏蔽桌面、任务栏的右键菜单,防止用户通过菜单操作退出。
隐藏任务栏和桌面图标:通过系统设置或脚本隐藏任务栏,删除所有桌面图标,使用户无法与系统交互。
应用程序设计(前端锁定)
(1)全屏独占模式
应用程序启动时自动进入无边框全屏模式,覆盖整个屏幕,隐藏标题栏、最小化 / 关闭按钮,用户无法通过窗口操作退出。
示例:使用Qt的FramelessWindowHint+showFullScreen(),或浏览器的kiosk模式(如 Chrome 的–kiosk参数)。
(2)拦截用户输入
屏蔽键盘事件:在应用程序中拦截可能用于退出的按键(如Esc、F1-F12)和组合键。
处理触摸 / 鼠标事件:禁止通过边缘滑动(如触摸屏的边缘手势)、右键点击等方式触发系统功能。
定时恢复机制:若用户通过特殊操作暂时离开应用(如某些系统漏洞),程序会定时检测并自动恢复到全屏状态。
(3)防止多窗口和进程切换
限制应用程序仅允许单实例运行,禁止新建窗口(如通过互斥锁机制)。
监控系统进程,若检测到任务管理器、浏览器等 “危险程序” 启动,立即强制关闭。
硬件与物理限制
禁用外部输入设备:通过 BIOS 或设备管理器禁用 USB 接口(防止插入 U 盘安装程序)、光驱、键盘某些按键(如Power键)。
定制化硬件:使用无物理按键的触摸屏设备,或封装主机(无外接接口),从物理层面阻止用户操作硬件退出。
后台监控与恢复机制
进程守护:运行一个后台服务(守护进程),实时监控主应用程序状态,若主程序被强制关闭,立即重启。
定时重置:设置定时任务(如每小时)自动重启系统或应用,清除用户可能的操作痕迹,恢复到初始状态。
远程管控:通过网络远程监控终端状态,若检测到异常(如用户破解锁定),可远程重启或执行预设指令。
网页程序如何禁止用户退出回到桌面
将网页程序内嵌到应用中并打包成安装包
一个基于PyQt5开发的全屏自助终端浏览器应用,主要用于创建一个安全、锁定的浏览环境,防止用户退出或访问系统功能。
功能特点
- 系统级键盘锁定:阻止Windows键、任务视图键、ESC键及各种组合键
- 浏览器安全限制:禁用右键菜单、快捷键和功能键
- 全屏锁定:无边框全屏显示,窗口始终置顶
- 单实例运行:确保只有一个浏览器实例运行
- 触摸事件处理:自定义处理触摸滑动事件
Python源码
import sys
import ctypes
import time
import sys
import os
import logging
import io
from logging.handlers import RotatingFileHandler
from ctypes import wintypes
from PyQt5.QtCore import Qt, QUrl, QEvent, QTimer, QPointF, QDateTime
from PyQt5.QtWidgets import QApplication, QMainWindow
from PyQt5.QtWebEngineWidgets import QWebEngineView, QWebEnginePage
from PyQt5.QtGui import QTouchEvent, QKeySequence
# 配置日志系统 - 增强版,确保目录可写
log_dir = os.path.join(os.path.expanduser("~"), "AppData", "Local", "baidu", "logs")
# 确保日志目录存在,如果主路径不可写则尝试备选路径
try:
os.makedirs(log_dir, exist_ok=True)
log_file = os.path.join(log_dir, "app.log")
except Exception as e:
# 如果无法创建主日志目录,使用临时目录作为备选
import tempfile
log_dir = os.path.join(tempfile.gettempdir(), "baidu", "logs")
os.makedirs(log_dir, exist_ok=True)
log_file = os.path.join(log_dir, "app.log")
print(f"无法在标准位置创建日志目录,使用临时目录: {log_dir}. 错误: {str(e)}")
# 创建日志记录器
logger = logging.getLogger("KioskBrowser")
logger.setLevel(logging.DEBUG) # 设置为DEBUG级别以捕获所有日志
# 创建处理器 - 写入文件
file_handler = None
try:
file_handler = RotatingFileHandler(log_file, maxBytes=5*1024*1024, backupCount=3)
file_handler.setLevel(logging.DEBUG)
# 添加到记录器
logger.addHandler(file_handler)
except Exception as e:
print(f"无法创建文件日志处理器: {str(e)}")
# 创建一个自定义的UTF-8编码StreamHandler类
class UTF8ConsoleHandler(logging.StreamHandler):
def __init__(self):
# 强制使用UTF-8编码
stream = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8')
super().__init__(stream)
# 创建控制台处理器
console_handler = UTF8ConsoleHandler()
console_handler.setLevel(logging.DEBUG)
# 设置日志格式
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
if file_handler:
file_handler.setFormatter(formatter)
console_handler.setFormatter(formatter)
# 添加控制台处理器到记录器
logger.addHandler(console_handler)
# 添加简单的日志函数,方便使用
def log_debug(message):
logger.debug(message)
def log_info(message):
logger.info(message)
def log_warning(message):
logger.warning(message)
def log_error(message):
logger.error(message)
def log_exception(message):
logger.exception(message)
# 可选的管理员权限检查(不再强制要求)
# 这样可以避免每次运行程序时弹出UAC确认窗口
def is_admin():
try:
result = ctypes.windll.shell32.IsUserAnAdmin()
log_debug(f"管理员权限检查结果: {result}")
return result
except Exception as e:
log_error(f"管理员权限检查异常: {str(e)}")
return False
# 不再强制要求管理员权限,允许在普通用户权限下运行程序
# 如果需要管理员权限功能,可以在需要时再请求
admin_mode = is_admin()
log_info(f"程序启动,管理员模式: {admin_mode}")
# 手动定义ULONG_PTR类型
if hasattr(ctypes, 'c_ulonglong'):
ULONG_PTR = ctypes.c_ulonglong
else:
ULONG_PTR = ctypes.c_ulong
# Windows API相关设置
user32 = ctypes.WinDLL('user32', use_last_error=True)
WH_KEYBOARD_LL = 13
WM_KEYDOWN = 0x0100
WM_SYSKEYDOWN = 0x0104
WM_KEYUP = 0x0101
WM_SYSKEYUP = 0x0105
VK_MENU = 0x12 # Alt键
VK_TAB = 0x09 # Tab键
VK_LWIN = 0x5B # 左Windows键
VK_RWIN = 0x5C # 右Windows键
VK_TASKVIEW = 0x5F # 任务视图键
VK_ESCAPE = 0x1B # ESC键
VK_F4 = 0x73 # F4键
# 定义钩子回调函数类型
HOOKPROC = ctypes.CFUNCTYPE(ctypes.c_int, ctypes.c_int, wintypes.WPARAM, wintypes.LPARAM)
class KBDLLHOOKSTRUCT(ctypes.Structure):
_fields_ = [
("vkCode", wintypes.DWORD),
("scanCode", wintypes.DWORD),
("flags", wintypes.DWORD),
("time", wintypes.DWORD),
("dwExtraInfo", ULONG_PTR)
]
# 全局变量用于跟踪Alt键状态
alt_pressed = False
def hook_callback(nCode, wParam, lParam):
global alt_pressed
try:
if nCode >= 0:
# 安全地转换lParam指针,避免整数溢出
try:
kbd = ctypes.cast(lParam, ctypes.POINTER(KBDLLHOOKSTRUCT)).contents
except OverflowError:
log_error("指针转换溢出,跳过本次键盘事件处理")
# 使用正确的类型调用CallNextHookEx,避免整数溢出
return user32.CallNextHookEx(None, ctypes.c_int(nCode), ctypes.c_void_p(wParam), ctypes.c_void_p(lParam))
# 阻止关键系统键
if kbd.vkCode in (VK_LWIN, VK_RWIN, VK_TASKVIEW, VK_ESCAPE):
log_debug(f"阻止系统键: {kbd.vkCode}")
return 1
# 检测Alt键状态
if kbd.vkCode == VK_MENU:
if wParam == WM_KEYDOWN or wParam == WM_SYSKEYDOWN:
alt_pressed = True
log_debug("Alt键按下")
elif wParam == WM_KEYUP or wParam == WM_SYSKEYUP:
alt_pressed = False
log_debug("Alt键释放")
# 阻止Alt+Tab, Alt+F4等组合键
if alt_pressed and kbd.vkCode in (VK_TAB, VK_F4):
log_debug(f"阻止组合键Alt+{kbd.vkCode}")
return 1
# 调用下一个钩子,确保所有非拦截的正常按键都能通过
# 使用正确的类型避免整数溢出
return user32.CallNextHookEx(None, ctypes.c_int(nCode), ctypes.c_void_p(wParam), ctypes.c_void_p(lParam))
except Exception as e:
log_exception(f"钩子回调异常: {str(e)}")
# 发生异常时,返回-1表示钩子处理失败,系统会调用下一个钩子
return -1
# 设置钩子
def set_hook():
try:
hook_proc = HOOKPROC(hook_callback)
hhook = user32.SetWindowsHookExW(WH_KEYBOARD_LL, hook_proc, None, 0)
if not hhook:
error_code = ctypes.get_last_error()
log_error(f"设置钩子失败,错误代码: {error_code}")
raise ctypes.WinError(error_code)
log_info("键盘钩子设置成功")
return hhook, hook_proc
except Exception as e:
log_exception(f"设置钩子异常: {str(e)}")
raise
class LockedWebPage(QWebEnginePage):
def __init__(self, parent=None):
super().__init__(parent)
# QWebEnginePage没有setAttribute方法,输入法支持通过WebView设置
# 具体输入法配置在FullscreenBrowser.setup_input_method_support中处理
pass
# 阻止右键菜单
def contextMenuEvent(self, event):
event.ignore()
# 处理页面内的键盘事件
def keyPressEvent(self, event):
# 阻止所有可能的退出快捷键
if (event.modifiers() & Qt.ControlModifier):
# 阻止Ctrl+N, Ctrl+T等新建窗口快捷键
if event.key() in (Qt.Key_N, Qt.Key_T, Qt.Key_W, Qt.Key_P):
event.ignore()
return
# 阻止缩放快捷键
if event.key() in (Qt.Key_Plus, Qt.Key_Minus, Qt.Key_Equal, Qt.Key_0):
event.ignore()
return
# 阻止F1-F12功能键
if event.key() >= Qt.Key_F1 and event.key() <= Qt.Key_F12:
event.ignore()
return
# 阻止Windows键
if event.key() in (Qt.Key_Super_L, Qt.Key_Super_R):
event.ignore()
return
# 阻止Alt+Tab
if event.key() == Qt.Key_Tab and (event.modifiers() & Qt.AltModifier):
event.ignore()
return
# 允许其他所有键盘输入
super().keyPressEvent(event)
# 自定义WebView,处理新窗口请求并增强输入法支持
class LockedWebView(QWebEngineView):
def __init__(self, parent=None):
super().__init__(parent)
# 设置输入法支持属性
self.setAttribute(Qt.WA_InputMethodEnabled, True)
self.setAttribute(Qt.WA_KeyCompression, False)
self.setFocusPolicy(Qt.StrongFocus)
# 重写创建新窗口的方法
def createWindow(self, windowType):
# 始终在当前窗口加载内容
self.new_window = LockedWebView()
self.new_window.urlChanged.connect(self._redirect_new_window)
return self.new_window
# 将新窗口的URL重定向到当前窗口
def _redirect_new_window(self, url):
if hasattr(self, 'new_window'):
self.load(url)
self.new_window.close()
del self.new_window
# 重写输入法查询方法,确保输入法能够获取正确的上下文信息
def inputMethodQuery(self, query, argument=None):
# 确保输入法查询能够获取正确的焦点位置和上下文
result = super().inputMethodQuery(query, argument)
# 添加更详细的查询类型日志
query_type = "未知"
if query == Qt.ImEnabled:
query_type = "ImEnabled (输入法启用状态)"
elif query == Qt.ImCursorRectangle:
query_type = "ImCursorRectangle (光标位置矩形)"
elif query == Qt.ImFont:
query_type = "ImFont (字体信息)"
elif query == Qt.ImSurroundingText:
query_type = "ImSurroundingText (周围文本)"
elif query == Qt.ImCurrentSelection:
query_type = "ImCurrentSelection (当前选择)"
elif query == Qt.ImMaximumTextLength:
query_type = "ImMaximumTextLength (最大文本长度)"
elif query == Qt.ImAnchorPosition:
query_type = "ImAnchorPosition (锚点位置)"
elif query == Qt.ImInputDirection:
query_type = "ImInputDirection (输入方向)"
elif query == Qt.ImHints:
query_type = "ImHints (输入法提示)"
# 使用安全的字符串格式避免编码问题
log_debug(f"输入法查询 - 类型: {query_type}, 查询代码: {query}")
return result
# 重写输入法事件处理方法
def inputMethodEvent(self, event):
# 记录输入法事件的基本信息
log_debug(f"输入法事件 - 文本: {event.text()}, 替换长度: {event.commitString().length() if hasattr(event, 'commitString') else '未知'}")
# 处理输入法事件
super().inputMethodEvent(event)
# 重写焦点事件,确保WebView获得焦点时输入法状态正确
def focusInEvent(self, event):
log_debug("WebView获得焦点 - 启用输入法支持")
# 确保焦点获得时输入法状态正确
self.setAttribute(Qt.WA_InputMethodEnabled, True)
self.setAttribute(Qt.WA_KeyCompression, False)
self.setFocusPolicy(Qt.StrongFocus)
super().focusInEvent(event)
def focusOutEvent(self, event):
log_debug("WebView失去焦点")
super().focusOutEvent(event)
class FullscreenBrowser(QMainWindow):
def __init__(self, url):
super().__init__()
log_info(f"初始化浏览器,加载URL: {url}")
# 触摸屏滑动检测变量
self.touch_start_pos = None
self.touch_start_time = 0
self.swipe_threshold = 50 # 滑动距离阈值
self.time_threshold = 500 # 滑动时间阈值(毫秒)
# 设置无边框窗口
self.setWindowFlags(Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint)
log_debug("设置无边框窗口和窗口置顶")
# 创建Web视图
self.web_view = LockedWebView()
self.web_view.setPage(LockedWebPage(self.web_view))
self.web_view.load(QUrl(url))
self.setCentralWidget(self.web_view)
log_debug("Web视图创建完成")
# 启用触摸事件处理
self.setAttribute(Qt.WA_AcceptTouchEvents)
self.web_view.setAttribute(Qt.WA_AcceptTouchEvents)
log_debug("启用触摸事件处理")
# 启用输入法支持 - 增强版
self.setAttribute(Qt.WA_InputMethodEnabled, True)
self.setAttribute(Qt.WA_KeyCompression, False) # 禁用键压缩,确保所有按键事件都能正确传递
self.web_view.setAttribute(Qt.WA_InputMethodEnabled, True)
self.web_view.setAttribute(Qt.WA_KeyCompression, False)
log_debug("启用输入法支持")
# 添加专门的输入法支持配置
self.setup_input_method_support()
def setup_input_method_support(self):
"""设置完整的输入法支持配置"""
# 为窗口设置输入法支持
self.setAttribute(Qt.WA_InputMethodEnabled, True)
self.setAttribute(Qt.WA_KeyCompression, False) # 禁用键压缩,确保所有按键事件都能正确传递
self.setAttribute(Qt.WA_MacShowFocusRect, True) # 显示焦点矩形
# 为WebEngineView添加额外的输入法配置
self.web_view.setFocusPolicy(Qt.StrongFocus) # 设置强焦点策略
self.web_view.setAcceptDrops(True) # 允许拖放
# 确保WebView可以处理所有输入事件
self.web_view.setAttribute(Qt.WA_InputMethodEnabled, True) # 启用输入法
self.web_view.setAttribute(Qt.WA_KeyCompression, False) # 禁用键压缩
self.web_view.setAttribute(Qt.WA_MacShowFocusRect, True) # 显示焦点矩形
# 注意:QWebEnginePage没有setInputMethodHints方法,输入法支持通过WebView设置
# 为WebView设置适合中文输入的输入法提示
# 移除ImhNoPredictiveText和ImhSensitiveData以允许显示候选词
# 使用PyQt5中实际存在的输入法提示标志
self.web_view.setInputMethodHints(Qt.ImhMultiLine | Qt.ImhNoAutoUppercase)
log_info("输入法支持配置完成")
# 进入全屏模式
self.showFullScreen()
log_info("进入全屏模式")
# 安装事件过滤器
self.installEventFilter(self)
self.web_view.installEventFilter(self)
log_debug("安装事件过滤器")
# 定时检查窗口状态
self.timer = QTimer(self)
self.timer.timeout.connect(self.check_status)
self.timer.start(200)
log_debug("启动窗口状态检查定时器")
def check_status(self):
try:
# 确保窗口始终全屏
if not self.isFullScreen():
self.showFullScreen()
log_debug("恢复全屏模式")
# 确保只有一个窗口实例
if len(QApplication.topLevelWidgets()) > 1:
extra_windows = [w for w in QApplication.topLevelWidgets() if w != self]
if extra_windows:
log_debug(f"检测到额外窗口,关闭 {len(extra_windows)} 个窗口")
for widget in extra_windows:
widget.close()
# 确保窗口置顶
self.raise_()
except Exception as e:
log_exception(f"检查窗口状态异常: {str(e)}")
# 事件过滤器 - 修复按键处理逻辑
def eventFilter(self, obj, event):
try:
# 处理触摸事件
if event.type() == QEvent.TouchBegin:
return self.handle_touch_begin(event)
elif event.type() == QEvent.TouchUpdate:
return self.handle_touch_update(event)
elif event.type() == QEvent.TouchEnd:
return self.handle_touch_end(event)
# 确保输入法事件能够正确传递 - 只保留PyQt5中实际存在的事件类型
if event.type() in (QEvent.InputMethodQuery, QEvent.InputMethod):
log_debug(f"输入法事件通过: {event.type()}")
return False # 允许输入法事件传递
# 处理手势事件
if event.type() in (QEvent.Gesture, QEvent.GestureOverride):
event.ignore()
return True
# 阻止窗口关闭
if event.type() == QEvent.Close:
log_debug("阻止窗口关闭")
event.ignore()
return True
# 处理键盘事件
if event.type() == QEvent.KeyPress:
key_name = QKeySequence(event.key()).toString()
modifiers = []
if event.modifiers() & Qt.ControlModifier:
modifiers.append("Ctrl")
if event.modifiers() & Qt.AltModifier:
modifiers.append("Alt")
if event.modifiers() & Qt.ShiftModifier:
modifiers.append("Shift")
if modifiers:
key_str = " + ".join(modifiers) + " + " + key_name
else:
key_str = key_name
# 记录按键信息,使用简化的格式避免编码问题
# 只记录按键代码和修饰键,不记录可能导致编码问题的字符
log_debug(f"按键事件: key_code={event.key()}, modifiers={event.modifiers()}")
# 只拦截特定的危险快捷键
should_block = False
# 阻止Alt+F4关闭
if event.key() == Qt.Key_F4 and (event.modifiers() & Qt.AltModifier):
log_info("阻止Alt+F4关闭")
event.ignore()
should_block = True
# 阻止Alt+Tab切换
elif event.key() == Qt.Key_Tab and (event.modifiers() & Qt.AltModifier):
log_info("阻止Alt+Tab切换")
event.ignore()
should_block = True
# 实现Shift+Tab退出全屏功能
elif event.key() == Qt.Key_Tab and (event.modifiers() & Qt.ShiftModifier):
log_info("检测到Shift+Tab,退出全屏模式")
self.showNormal() # 退出全屏模式
should_block = True
# 对于被拦截的按键返回True,其他按键返回False允许传播
return should_block
except Exception as e:
log_exception(f"事件过滤器异常: {str(e)}")
# 发生异常时,返回False以确保按键不会被意外拦截
return False
# 对于所有其他类型的事件,调用父类的eventFilter方法
return super().eventFilter(obj, event)
# 处理触摸开始事件
def handle_touch_begin(self, event):
touch_points = event.touchPoints()
if touch_points:
if hasattr(touch_points[0], 'pos'):
self.touch_start_pos = touch_points[0].pos()
else:
self.touch_start_pos = QPointF(touch_points[0].x(), touch_points[0].y())
self.touch_start_time = QDateTime.currentMSecsSinceEpoch()
return False
# 处理触摸更新事件
def handle_touch_update(self, event):
if self.touch_start_pos is None:
return False
touch_points = event.touchPoints()
if touch_points:
if hasattr(touch_points[0], 'pos'):
current_pos = touch_points[0].pos()
else:
current_pos = QPointF(touch_points[0].x(), touch_points[0].y())
dx = current_pos.x() - self.touch_start_pos.x()
if dx > self.swipe_threshold:
event.ignore()
return True
return False
# 处理触摸结束事件
def handle_touch_end(self, event):
if self.touch_start_pos is None:
self.touch_start_pos = None
return False
touch_points = event.touchPoints()
if touch_points:
if hasattr(touch_points[0], 'pos'):
end_pos = touch_points[0].pos()
else:
end_pos = QPointF(touch_points[0].x(), touch_points[0].y())
dx = end_pos.x() - self.touch_start_pos.x()
dt = QDateTime.currentMSecsSinceEpoch() - self.touch_start_time
if dx > self.swipe_threshold and dt < self.time_threshold:
event.ignore()
self.touch_start_pos = None
return True
self.touch_start_pos = None
return False
def main():
try:
# 确保只运行一个实例
instance_check = ctypes.windll.kernel32.CreateMutexW(None, True, "KioskBrowserMutex")
if ctypes.GetLastError() == 183: # 已存在实例
log_info("检测到程序已在运行,退出")
sys.exit(0)
log_info("程序启动,实例检查通过")
# 设置系统级钩子
hhook = None
hook_proc = None
try:
hhook, hook_proc = set_hook()
except Exception as e:
log_error(f"无法设置钩子: {str(e)}")
hhook = None
app = QApplication(sys.argv)
app.setApplicationName("KioskBrowser")
# 设置应用程序字体,确保中文显示正常
from PyQt5.QtGui import QFont
font = QFont()
# 尝试设置常见的中文字体
for font_family in ["SimHei", "WenQuanYi Micro Hei", "Heiti TC", "Microsoft YaHei"]:
temp_font = QFont(font_family)
if temp_font.exactMatch():
font = temp_font
log_info(f"设置中文字体: {font_family}")
break
app.setFont(font)
log_info("Qt应用程序对象创建成功")
# 加载网站
browser = FullscreenBrowser("https://www.baidu.com")
log_info("浏览器窗口创建成功")
# 确保主窗口和WebView获得焦点,便于输入法工作
browser.activateWindow()
browser.web_view.setFocus()
# 添加短暂延迟后再次设置焦点,确保输入法能够正确初始化
def delayed_focus():
browser.web_view.setFocus()
log_debug("延迟设置WebView焦点")
QTimer.singleShot(500, delayed_focus)
# 运行应用程序
log_info("开始运行应用程序主循环")
result = app.exec_()
log_info(f"应用程序退出,返回代码: {result}")
# 移除钩子
if hhook:
user32.UnhookWindowsHookEx(hhook)
log_info("钩子已移除")
sys.exit(result)
except Exception as e:
log_exception(f"主函数异常: {str(e)}")
# 在发生严重异常时显示错误信息
import traceback
error_msg = traceback.format_exc()
print(f"程序异常: {error_msg}")
# 创建一个简单的消息框显示错误
try:
from PyQt5.QtWidgets import QMessageBox
msg_box = QMessageBox()
msg_box.setIcon(QMessageBox.Critical)
msg_box.setText(f"程序发生错误")
msg_box.setDetailedText(error_msg)
msg_box.setWindowTitle("错误")
msg_box.exec_()
except:
pass
sys.exit(1)
if __name__ == "__main__":
main()
安装方法
使用pip安装
pip install .
生成可执行文件
pyinstaller --onefile --windowed kiosk_browser.py
然后会生成一个exe执行文件到 dist 目录中,可以使用 Inno Setup 将执行文件打包成一个安装包。
打包配置文件 setup.iss 如下。
; 有关创建 Inno Setup 脚本文件的详细资料请查阅帮助文档!
#define MyAppName "baidu"
#define MyAppVersion "1.0"
#define MyAppPublisher "baidu"
#define MyAppURL "https://www.abc.com/"
#define MyAppExeName "baidu.exe"
[Setup]
; 注: AppId的值为单独标识该应用程序。
; 不要为其他安装程序使用相同的AppId值。
; (生成新的GUID,点击 工具|生成GUID 来获取。)
AppId={{6D8D2011-2F0E-4579-B0F0-4E3F2F2D3E48}}
AppName={#MyAppName}
AppVersion={#MyAppVersion}
AppPublisher={#MyAppPublisher}
AppPublisherURL={#MyAppURL}
AppSupportURL={#MyAppURL}
AppUpdatesURL={#MyAppURL}
DefaultDirName={pf}\{#MyAppName}
DefaultGroupName={#MyAppName}
AllowNoIcons=yes
OutputBaseFilename=baidu
Compression=lzma
SolidCompression=yes
PrivilegesRequired=lowest
[Languages]
Name: "english"; MessagesFile: "compiler:Default.isl"
[Tasks]
Name: "desktopicon"; Description: "Create desktop shortcut"; GroupDescription: "Additional icons"; Flags: unchecked
[Files]
Source: "dist\{#MyAppExeName}"; DestDir: "{app}"; Flags: ignoreversion
[Icons]
Name: "{group}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"
Name: "{commondesktop}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"; Tasks: desktopicon
[Run]
Filename: "{app}\{#MyAppExeName}"; Description: "Run {#MyAppName}"; Flags: nowait postinstall skipifsilent
[UninstallDelete]
Type: filesandordirs; Name: "{app}";
更多推荐



所有评论(0)