【LG007.控件篇】简单控件之Desktop

本章节介绍LGame如何管理控件与游戏对象的绘制流程,阐述Desktop作为控件统一管理器的作用,以及如何通过Desktop实现控件的统一调用和事件传递。

【LGame学堂007】简单控件之Desktop


这章开始,我们来玩玩几个简单的控件~不过,我想先装装大牛,给大家讲讲LGame是如何管理控件的~

声明哈,我没有深入地看代码,所以真的是简单地解释一下而已(好吧,我的废话真的很多的,希望不会引起大家的愤怒...)。

们试想一下,控件其实是什么?是对象咯~好吧,我废话了。控件要怎么在游戏里展示出来呢?当然少不了draw这个步骤~再试想一下,游戏里的控件数量不会只有1、2个吧?
好了,几十上百个控件还是会有的,这么多的控件,不可能东一个西一个的吧~当然要统一管理了。于是,就有了它——Desktop

有的控件都会丢进Desktop里面,由Desktop来统一调用各个控件的绘制函数,以便将控件绘制到手机屏幕中。还有一些触屏事件、点击事件等都是通过Desktop来传递给各个控件的。

实可以从Desktop的名字很形象地想到,桌面?顶部桌面?是的~ Desktop就像一张桌面,上面摆满了各种控件。

好啦,小弟也只是理解了Desktop的皮毛而已,希望大牛出来指点~嘻嘻

哎吖,这章的内容有点少呢,不太像我唠叨的风格~
好吧,这次就饶了你们吧~下次我尽量多唠叨一点~

原文第一次发布于Android-fans论坛:

http://www.android-fans.net/thread-1563-1-1.html

<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>透明桌面小人</title> <!-- Tailwind CSS (使用生产环境兼容的CDN) --> <link href="https://cdn.jsdelivr.net/npm/tailwindcss@3.4.1/dist/tailwind.min.css" rel="stylesheet"> <!-- Font Awesome --> <link href="https://cdn.jsdelivr.net/npm/font-awesome@4.7.0/css/font-awesome.min.css" rel="stylesheet"> <!-- Spine Player --> <script src="spine-player.js"></script> <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@esotericsoftware/spine-player@4.2.21/dist/spine-player.min.css"> <script> tailwind.config = { theme: { extend: { colors: { primary: '#6366f1', secondary: '#8b5cf6', }, fontFamily: { sans: ['Inter', 'system-ui', 'sans-serif'], } } } } </script> <style type="text/tailwindcss"> @layer utilities { .glass-effect { background: rgba(255, 255, 255, 0.2); backdrop-filter: blur(10px); -webkit-backdrop-filter: blur(10px); border: 1px solid rgba(255, 255, 255, 0.3); } .character-container { cursor: move; } .emoji-button { transition: all 0.2s ease; } .emoji-button:hover { transform: scale(1.2); } .emoji-button.active { border: 2px solid #6366f1; box-shadow: 0 0 10px rgba(99, 102, 241, 0.5); } } body { margin: 0; padding: 0; background-color: rgba(0, 0, 0, 0); overflow: hidden; } #character-container { position: absolute; top: 0; left: 0; width: 300px; height: 400px; z-index: 1000; user-select: none; } #spine-container { width: 100%; height: 350px; position: relative; } #controls { position: absolute; bottom: 0; left: 0; width: 100%; padding: 10px; display: flex; justify-content: center; gap: 10px; background: rgba(255, 255, 255, 0.1); backdrop-filter: blur(5px); -webkit-backdrop-filter: blur(5px); border-top: 1px solid rgba(255, 255, 255, 0.2); } .emoji-btn { width: 40px; height: 40px; border-radius: 50%; display: flex; align-items: center; justify-content: center; font-size: 20px; cursor: pointer; background: rgba(255, 255, 255, 0.2); border: none; transition: all 0.2s ease; } .emoji-btn:hover { transform: scale(1.2); background: rgba(255, 255, 255, 0.3); } .emoji-btn.active { background: rgba(99, 102, 241, 0.3); border: 2px solid #6366f1; } #close-btn { position: absolute; top: 5px; right: 5px; width: 30px; height: 30px; border-radius: 50%; background: rgba(255, 255, 255, 0.2); display: flex; align-items: center; justify-content: center; cursor: pointer; z-index: 10; transition: all 0.2s ease; } #close-btn:hover { background: rgba(255, 0, 0, 0.3); } </style> </head> <body> <div id="character-container"> <div id="close-btn"> <i class="fa fa-times" aria-hidden="true"></i> </div> <div id="spine-container"></div> <div id="controls"> <button class="emoji-btn active" data-emoji="emoji_0">😊</button> <button class="emoji-btn" data-emoji="emoji_1">😂</button> <button class="emoji-btn" data-emoji="emoji_2">😍</button> <button class="emoji-btn" data-emoji="emoji_3">😢</button> <button class="emoji-btn" data-emoji="emoji_4">😡</button> </div> </div> <script> // 浏览器兼容性检查 function checkBrowserCompatibility() { // 检测浏览器类型和版本 const browserInfo = getBrowserInfo(); // 检查WebGL支持 function isWebGLSupported() { try { const canvas = document.createElement('canvas'); return !!(window.WebGLRenderingContext && (canvas.getContext('webgl') || canvas.getContext('experimental-webgl'))); } catch (e) { return false; } } // 检查backdrop-filter支持 function checkBackdropFilterSupport() { if (typeof CSS !== 'undefined' && typeof CSS.supports !== 'undefined') { return CSS.supports('backdrop-filter', 'blur(10px)') || CSS.supports('-webkit-backdrop-filter', 'blur(10px)'); } return false; } // 检查必要的API支持 const requiredAPIs = { 'requestAnimationFrame': typeof window.requestAnimationFrame !== 'undefined', 'WebGL': isWebGLSupported(), 'Canvas': typeof HTMLCanvasElement !== 'undefined', 'TypedArrays': typeof Uint8Array !== 'undefined', 'backdrop-filter': checkBackdropFilterSupport() }; // 检查是否所有必要API都支持 const allSupported = Object.values(requiredAPIs).every(val => val); // 针对Edge浏览器的特定检查和处理 if (browserInfo.name === 'Edge') { console.log(`检测到Edge浏览器,版本: ${browserInfo.version}`); // Edge特定的兼容性处理 handleEdgeSpecificIssues(browserInfo.version); // 如果是Edge浏览器,即使WebGL检测失败,也尝试继续 if (!requiredAPIs.WebGL) { console.warn('Edge浏览器中WebGL检测失败,尝试使用软件渲染'); // 在Edge中,即使WebGL检测失败,也尝试初始化 requiredAPIs.WebGL = true; // 标记为支持,以便继续 } } if (!allSupported) { console.warn('浏览器兼容性问题:', requiredAPIs); // 显示兼容性警告 const warningDiv = document.createElement('div'); warningDiv.className = 'fixed top-0 left-0 w-full bg-red-500 text-white z-50 flex items-center justify-center'; warningDiv.innerHTML = ` <div class="p-4 max-w-md"> <h3 class="text-xl font-bold mb-2">浏览器兼容性问题</h3> <p>您的浏览器可能不支持某些功能,应用可能无法正常工作。</p> <p class="mt-2">建议使用最新版本的Chrome、Firefox、Edge或Safari浏览器。</p> ${browserInfo.name === 'Edge' ? ` <p class="mt-2 text-yellow-200">Edge浏览器提示:</p> <ul class="mt-1 list-disc list-inside text-sm text-yellow-200"> <li>确保已启用硬件加速</li> <li>更新显卡驱动程序</li> <li>尝试重启浏览器</li> </ul> ` : ''} <button id="close-warning" class="mt-4 px-4 py-2 bg-white text-red-500 rounded-full">关闭</button> </div> `; document.body.appendChild(warningDiv); // 关闭警告按钮 document.getElementById('close-warning').addEventListener('click', () => { warningDiv.remove(); }); } return true; // 即使有兼容性问题,也尝试继续运行 } // 获取浏览器信息 function getBrowserInfo() { const userAgent = navigator.userAgent; let browserName = '未知浏览器'; let browserVersion = '未知版本'; // 检测浏览器类型 if (userAgent.includes('Chrome')) { browserName = 'Chrome'; browserVersion = userAgent.match(/Chrome\/(\d+)/)[1]; } else if (userAgent.includes('Firefox')) { browserName = 'Firefox'; browserVersion = userAgent.match(/Firefox\/(\d+)/)[1]; } else if (userAgent.includes('Safari')) { browserName = 'Safari'; browserVersion = userAgent.match(/Version\/(\d+)/)[1]; } else if (userAgent.includes('Edge')) { browserName = 'Edge'; browserVersion = userAgent.match(/Edge\/(\d+)/) ? userAgent.match(/Edge\/(\d+)/)[1] : (userAgent.match(/Edg\/(\d+)/) ? userAgent.match(/Edg\/(\d+)/)[1] : '未知'); } else if (userAgent.includes('Opera') || userAgent.includes('OPR')) { browserName = 'Opera'; browserVersion = userAgent.match(/(Opera|OPR)\/(\d+)/)[2]; } return { name: browserName, version: browserVersion }; } // 处理Edge特定问题 function handleEdgeSpecificIssues(version) { const edgeVersion = parseInt(version); // Edge旧版本处理 if (edgeVersion < 79) { console.warn('检测到旧版本Edge浏览器,可能存在兼容性问题'); // 为旧版本Edge添加backdrop-filter回退样式 addEdgeBackdropFilterFallback(); } // 修复Edge中的WebGL问题 fixEdgeWebGLIssues(); } // 为旧版本版本Edge添加backdrop-filter回退样式 function addEdgeBackdropFilterFallback() { const style = document.createElement('style'); style.textContent = ` /* Edge旧版本backdrop-filter回退样式 */ .glass-effect, #controls, #close-btn { background: rgba(255, 255, 255, 0.3) !important; } `; document.head.appendChild(style); } // 修复Edge WebGL问题 function fixEdgeWebGLIssues() { // Edge中WebGL上下文获取可能问题的修复 const originalGetContext = HTMLCanvasElement.prototype.getContext; HTMLCanvasElement.prototype.getContext = function(contextId, options) { try { // 尝试获取WebGL上下文 const context = originalGetContext.call(this, contextId, options); if (context) { return context; } // 如果失败,尝试使用experimental-webgl if (contextId === 'webgl') { console.log('尝试使用experimental-webgl'); return originalGetContext.call(this, 'experimental-webgl', options); } } catch (error) { console.error('获取WebGL上下文失败:', error); // 在Edge中,尝试使用不同的参数 if (contextId === 'webgl' || contextId === 'experimental-webgl') { try { console.log('尝试使用简化参数获取WebGL上下文'); return originalGetContext.call(this, contextId, { alpha: true, antialias: false, depth: false, stencil: false, preserveDrawingBuffer: true }); } catch (error2) { console.error('使用简化参数获取WebGL上下文也失败:', error2); } } } return null; }; console.log('Edge WebGL修复已应用'); } // 全局变量 let player; let isDragging = false; let offsetX, offsetY; let currentEmoji = 'emoji_0'; let animationFrameId = null; // DOM元素 const characterContainer = document.getElementById('character-container'); const spineContainer = document.getElementById('spine-container'); const closeBtn = document.getElementById('close-btn'); const emojiBtns = document.querySelectorAll('.emoji-btn'); // 初始化Spine动画 function initSpine() { try { // 获取浏览器信息 const browserInfo = getBrowserInfo(); // 创建Spine播放器配置 const playerConfig = { jsonUrl: "assets/1310.json", atlasUrl: "assets/1310.atlas", alpha: true, backgroundColor: "#00000000", preserveDrawingBuffer: true, showLoading: false, showControls: false, animation: "idle", success: function(player) { console.log("Spine动画加载成功"); // 默认显示第一个表情 setEmoji(currentEmoji); // 启动动画循环 startAnimationLoop(); }, error: function(reason) { console.error("Spine动画加载失败:", reason); // 显示错误信息 showError(`动画加载失败: ${reason.message || reason}`); // 回退到简单的表情显示 fallbackToSimpleEmoji(); }, viewport: { padLeft: "0%", padRight: "0%", padTop: "0%", padBottom: "0%" } }; // Edge特定配置 if (browserInfo.name === 'Edge') { console.log('为Edge浏览器应用特定配置'); // 在Edge中使用更简单的配置 playerConfig.preserveDrawingBuffer = true; playerConfig.alpha = true; // 为Edge添加额外的错误处理 const originalErrorHandler = playerConfig.error; playerConfig.error = function(reason) { console.error("Edge中Spine动画加载失败:", reason); // Edge特定的错误处理 if (reason.message && reason.message.includes('WebGL')) { showError(`WebGL错误: ${reason.message}<br>在Edge中,您可能需要启用硬件加速或更新显卡驱动。`); } else { showError(`动画加载失败: ${reason.message || reason}`); } // 回退到简单的表情显示 fallbackToSimpleEmoji(); // 调用原始错误处理 if (originalErrorHandler) originalErrorHandler(reason); }; } // 创建Spine播放器 player = new spine.SpinePlayer("spine-container", playerConfig); } catch (error) { console.error("Spine初始化异常:", error); // 获取浏览器信息 const browserInfo = getBrowserInfo(); if (browserInfo.name === 'Edge' && error.message.includes('WebGL')) { showError(`初始化失败: ${error.message}<br>在Edge中,您可能需要启用硬件加速或更新显卡驱动。`); } else { showError(`初始化失败: ${error.message}`); } fallbackToSimpleEmoji(); } } // 显示错误信息 function showError(message) { const errorDiv = document.createElement('div'); errorDiv.className = 'absolute inset-0 bg-red-900 bg-opacity-80 text-white flex flex-col items-center justify-center p-4 z-10'; errorDiv.innerHTML = ` <h3 class="text-xl font-bold mb-2">加载错误</h3> <p class="text-center mb-4">${message}</p> <p class="text-sm text-gray-300">请尝试检查Spine资源文件是否存在或格式是否正确</p> `; document.getElementById('spine-container').appendChild(errorDiv); } // 回退到简单的表情显示 function fallbackToSimpleEmoji() { const container = document.getElementById('spine-container'); container.innerHTML = ''; const emojiDiv = document.createElement('div'); emojiDiv.id = 'fallback-emoji'; emojiDiv.className = 'w-full h-full flex items-center justify-center text-6xl'; emojiDiv.textContent = '😊'; container.appendChild(emojiDiv); // 更新表情的函数 window.setFallbackEmoji = function(emojiName) { const emojiMap = { 'emoji_0': '😊', 'emoji_1': '😂', 'emoji_2': '😍', 'emoji_3': '😢', 'emoji_4': '😡' }; emojiDiv.textContent = emojiMap[emojiName] || '😊'; }; console.log("已回退到简单表情模式"); } // 动画循环 function startAnimationLoop() { let lastTime = 0; function animate(currentTime) { if (!lastTime) lastTime = currentTime; const delta = (currentTime - lastTime) / 1000; // 转换为秒 lastTime = currentTime; if (player && player.update) { try { player.update(delta); } catch (error) { console.error("动画更新错误:", error); } } animationFrameId = requestAnimationFrame(animate); } // 停止之前的动画循环 if (animationFrameId) { cancelAnimationFrame(animationFrameId); } animationFrameId = requestAnimationFrame(animate); } // 设置表情 function setEmoji(emojiName) { currentEmoji = emojiName; // 更新按钮状态 emojiBtns.forEach(btn => { if (btn.dataset.emoji === emojiName) { btn.classList.add('active'); } else { btn.classList.remove('active'); } }); // 检查是否在回退模式 if (window.setFallbackEmoji) { window.setFallbackEmoji(emojiName); return; } if (!player) return; try { // 在Spine中设置表情 // 这里假设Spine动画中有对应的皮肤或附件来切换表情 // 如果是通过皮肤切换 if (player.skeleton && player.skeleton.data && player.skeleton.data.findSkin) { if (player.skeleton.data.findSkin(emojiName)) { player.skeleton.setSkin(emojiName); player.skeleton.setToSetupPose(); if (player.update) player.update(0); return; } } // 如果是通过附件切换 if (player.skeleton && player.skeleton.findSlot) { const slot = player.skeleton.findSlot("face"); if (slot && player.skeleton.getAttachment) { const attachment = player.skeleton.getAttachment(slot.index, emojiName); if (attachment) { slot.setAttachment(attachment); if (player.update) player.update(0); return; } } } // 如果是通过动画切换 if (player.animationState && player.animationState.data && player.animationState.data.findAnimation) { if (player.animationState.data.findAnimation(emojiName)) { player.animationState.setAnimation(0, emojiName, false); // 播放完表情动画后回到idle if (player.animationState.data.findAnimation("idle")) { player.animationState.addAnimation(0, "idle", true, 0); } return; } } console.warn(`未找到表情: ${emojiName}`); } catch (error) { console.error("设置表情错误:", error); } } // 窗口拖动功能 function initDrag() { characterContainer.addEventListener('mousedown', startDrag); characterContainer.addEventListener('touchstart', startDrag, { passive: false }); function startDrag(e) { // 忽略控件区域的拖动 if (e.target.closest('#controls') || e.target.closest('#close-btn')) { return; } isDragging = true; if (e.type === 'mousedown') { offsetX = e.clientX - characterContainer.getBoundingClientRect().left; offsetY = e.clientY - characterContainer.getBoundingClientRect().top; document.addEventListener('mousemove', drag); document.addEventListener('mouseup', stopDrag); } else { offsetX = e.touches[0].clientX - characterContainer.getBoundingClientRect().left; offsetY = e.touches[0].clientY - characterContainer.getBoundingClientRect().top; document.addEventListener('touchmove', drag, { passive: false }); document.addEventListener('touchend', stopDrag); } e.preventDefault(); } function drag(e) { if (!isDragging) return; let clientX, clientY; if (e.type === 'mousemove') { clientX = e.clientX; clientY = e.clientY; } else { clientX = e.touches[0].clientX; clientY = e.touches[0].clientY; e.preventDefault(); // 防止滚动 } const x = clientX - offsetX; const y = clientY - offsetY; // 限制在屏幕范围内 const maxX = window.innerWidth - characterContainer.offsetWidth; const maxY = window.innerHeight - characterContainer.offsetHeight; const boundedX = Math.max(0, Math.min(x, maxX)); const boundedY = Math.max(0, Math.min(y, maxY)); characterContainer.style.left = `${boundedX}px`; characterContainer.style.top = `${boundedY}px`; } function stopDrag() { isDragging = false; document.removeEventListener('mousemove', drag); document.removeEventListener('mouseup', stopDrag); document.removeEventListener('touchmove', drag); document.removeEventListener('touchend', stopDrag); } } // 初始化关闭按钮 function initCloseBtn() { closeBtn.addEventListener('click', () => { // 在Electron环境中关闭窗口 if (window.electron) { window.electron.closeWindow(); } else { // 在浏览器中隐藏 characterContainer.style.display = 'none'; console.log('窗口已关闭'); } }); } // 初始化表情按钮 function initEmojiBtns() { emojiBtns.forEach(btn => { btn.addEventListener('click', () => { setEmoji(btn.dataset.emoji); }); }); } // 窗口大小调整 function initResize() { // 简单的缩放功能,通过滚轮缩放角色大小 characterContainer.addEventListener('wheel', (e) => { e.preventDefault(); const currentWidth = characterContainer.offsetWidth; const currentHeight = characterContainer.offsetHeight; // 缩放因子 const scaleFactor = e.deltaY > 0 ? 0.9 : 1.1; const newWidth = Math.max(100, Math.min(currentWidth * scaleFactor, 500)); const newHeight = Math.max(150, Math.min(currentHeight * scaleFactor, 700)); characterContainer.style.width = `${newWidth}px`; characterContainer.style.height = `${newHeight}px`; // 调整Spine容器高度 const spineHeight = newHeight - 60; // 减去控制栏高度 document.getElementById('spine-container').style.height = `${spineHeight}px`; // 如果播放器已初始化,重新调整大小 if (player && player.resize) { player.resize(); } }); } // 初始化 document.addEventListener('DOMContentLoaded', () => { // 获取浏览器信息 const browserInfo = getBrowserInfo(); console.log(`当前浏览器: ${browserInfo.name} ${browserInfo.version}`); // 检查浏览器兼容性 const isCompatible = checkBrowserCompatibility(); // 为Edge浏览器添加特定样式 if (browserInfo.name === 'Edge') { document.body.classList.add('edge-browser'); // 添加Edge特定的CSS样式 const edgeStyle = document.createElement('style'); edgeStyle.textContent = ` /* Edge浏览器特定样式 */ .edge-browser #controls, .edge-browser #close-btn { -webkit-backdrop-filter: blur(10px); backdrop-filter: blur(10px); } /* Edge中可能需要调整z-index */ .edge-browser #character-container { z-index: 2147483647; } `; document.head.appendChild(edgeStyle); } // 初始化Spine initSpine(); // 初始化其他功能 initDrag(); initCloseBtn(); initEmojiBtns(); initResize(); // 随机位置初始化 const randomX = Math.floor(Math.random() * (window.innerWidth - 300)); const randomY = Math.floor(Math.random() * (window.innerHeight - 400)); characterContainer.style.left = `${randomX}px`; characterContainer.style.top = `${randomY}px`; }); // 页面卸载时清理 window.addEventListener('beforeunload', () => { if (animationFrameId) { cancelAnimationFrame(animationFrameId); } }); // 与Electron主进程通信 if (window.electron) { // 接收主进程的消息 window.electron.receive('update-emoji', (emoji) => { setEmoji(emoji); }); window.electron.receive('update-position', (x, y) => { characterContainer.style.left = `${x}px`; characterContainer.style.top = `${y}px`; }); } </script> </body> </html> import sys import os import json from pathlib import Path from PyQt5.QtWidgets import QApplication, QMainWindow, QWidget, QVBoxLayout from PyQt5.QtWebEngineWidgets import QWebEngineView from PyQt5.QtCore import Qt, QUrl, QPoint, pyqtSlot from PyQt5.QtGui import QRegion, QPixmap, QPainter, QColor class TransparentWindow(QMainWindow): def __init__(self): super().__init__() # 初始化窗口设置 self.init_window() # 初始化Web视图 self.init_web_view() # 加载配置 self.load_config() # 设置窗口位置 self.set_initial_position() def init_window(self): # 设置窗口标志 self.setWindowFlags( Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint | Qt.SubWindow ) # 设置窗口透明 self.setAttribute(Qt.WA_TranslucentBackground) self.setAttribute(Qt.WA_NoSystemBackground, False) # 设置窗口大小 self.setFixedSize(300, 400) # 创建中心部件 self.central_widget = QWidget() self.setCentralWidget(self.central_widget) self.layout = QVBoxLayout(self.central_widget) self.layout.setContentsMargins(0, 0, 0, 0) def init_web_view(self): # 创建WebEngineView self.web_view = QWebEngineView() # 设置Web通道用于与JavaScript通信 from PyQt5.QtWebChannel import QWebChannel self.channel = QWebChannel() self.channel.registerObject("backend", self) self.web_view.page().setWebChannel(self.channel) # 设置WebEngine设置 settings = self.web_view.settings() settings.setAttribute(settings.JavascriptEnabled, True) settings.setAttribute(settings.LocalContentCanAccessRemoteUrls, True) settings.setAttribute(settings.LocalContentCanAccessFileUrls, True) # 添加到布局 self.layout.addWidget(self.web_view) # 加载HTML文件 html_path = Path(__file__).parent / "desktop-character.html" self.web_view.load(QUrl.fromLocalFile(str(html_path))) def load_config(self): # 加载配置文件 config_path = Path(__file__).parent / "config.json" if config_path.exists(): with open(config_path, 'r', encoding='utf-8') as f: self.config = json.load(f) else: self.config = { "position": {"x": 100, "y": 100}, "emoji": "emoji_0", "size": {"width": 300, "height": 400} } def save_config(self): # 保存配置文件 config_path = Path(__file__).parent / "config.json" with open(config_path, 'w', encoding='utf-8') as f: json.dump(self.config, f, ensure_ascii=False, indent=2) def set_initial_position(self): # 设置初始位置 x = self.config.get("position", {}).get("x", 100) y = self.config.get("position", {}).get("y", 100) self.move(x, y) @pyqtSlot() def closeWindow(self): # 保存当前位置 pos = self.pos() self.config["position"] = {"x": pos.x(), "y": pos.y()} self.save_config() # 关闭窗口 self.close() @pyqtSlot(str) def updateEmoji(self, emoji): # 更新表情配置 self.config["emoji"] = emoji self.save_config() def mousePressEvent(self, event): # 记录鼠标按下时的位置 if event.button() == Qt.LeftButton: self.drag_position = event.globalPos() - self.frameGeometry().topLeft() event.accept() def mouseMoveEvent(self, event): # 拖动窗口 if event.buttons() == Qt.LeftButton: self.move(event.globalPos() - self.drag_position) event.accept() def mouseReleaseEvent(self, event): # 保存位置 if event.button() == Qt.LeftButton: pos = self.pos() self.config["position"] = {"x": pos.x(), "y": pos.y()} self.save_config() def main(): # 设置环境变量以支持透明背景 os.environ["QTWEBENGINE_CHROMIUM_FLAGS"] = "--enable-transparent-visuals --disable-gpu" app = QApplication(sys.argv) # 设置应用程序样式 app.setStyleSheet(""" QMainWindow { background: transparent; } QWebEngineView { background: transparent; } """) window = TransparentWindow() window.show() sys.exit(app.exec_()) if __name__ == "__main__": main() 浏览器兼容性问题 您的浏览器可能不支持某些功能,应用可能无法正常工作。 建议使用最新版本的Chrome、Firefox、Edge或Safari浏览器。
11-06
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值