上传文件至 /
This commit is contained in:
957
Setup.py
Normal file
957
Setup.py
Normal file
@@ -0,0 +1,957 @@
|
||||
import os
|
||||
import sys
|
||||
import time
|
||||
import requests
|
||||
import shutil
|
||||
from PyQt5.QtWidgets import (QApplication, QMainWindow, QLabel, QLineEdit,
|
||||
QPushButton, QVBoxLayout, QHBoxLayout, QWidget,
|
||||
QFileDialog, QProgressBar, QMessageBox, QFrame,
|
||||
QCheckBox, QStackedWidget, QTextEdit, QScrollArea)
|
||||
from PyQt5.QtCore import Qt, QThread, pyqtSignal, QTimer, QUrl
|
||||
from PyQt5.QtGui import (QFont, QIcon, QPalette, QColor, QPixmap,
|
||||
QImageReader, QImage, QIcon as QtGuiQIcon)
|
||||
|
||||
# --------------------------- 全局样式配置 ---------------------------
|
||||
GLOBAL_STYLE = """
|
||||
/* 主按钮样式:科技蓝底色+圆角 */
|
||||
QPushButton#PrimaryBtn {
|
||||
background-color: #1E88E5;
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
padding: 8px 16px;
|
||||
font-family: "微软雅黑", "Segoe UI";
|
||||
font-size: 10pt;
|
||||
}
|
||||
QPushButton#PrimaryBtn:hover {
|
||||
background-color: #1976D2;
|
||||
}
|
||||
QPushButton#PrimaryBtn:disabled {
|
||||
background-color: #BBDEFB;
|
||||
}
|
||||
|
||||
/* 次要按钮样式:灰色边框+透明底色 */
|
||||
QPushButton#SecondaryBtn {
|
||||
background-color: transparent;
|
||||
color: #333333;
|
||||
border: 1px solid #CCCCCC;
|
||||
border-radius: 4px;
|
||||
padding: 8px 16px;
|
||||
font-family: "微软雅黑", "Segoe UI";
|
||||
font-size: 10pt;
|
||||
}
|
||||
QPushButton#SecondaryBtn:hover {
|
||||
background-color: #F5F5F5;
|
||||
}
|
||||
|
||||
/* 进度条样式:蓝色进度+圆角 */
|
||||
QProgressBar {
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
background-color: #F5F5F5;
|
||||
height: 8px;
|
||||
font-family: "微软雅黑";
|
||||
font-size: 8pt;
|
||||
}
|
||||
QProgressBar::chunk {
|
||||
background-color: #1E88E5;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
/* 输入框样式:细边框+聚焦高亮 */
|
||||
QLineEdit {
|
||||
border: 1px solid #CCCCCC;
|
||||
border-radius: 4px;
|
||||
padding: 6px 10px;
|
||||
font-family: "微软雅黑";
|
||||
font-size: 9pt;
|
||||
}
|
||||
QLineEdit:focus {
|
||||
border-color: #1E88E5;
|
||||
}
|
||||
|
||||
/* 面板样式:圆角+浅灰底色 */
|
||||
QFrame#CardFrame {
|
||||
background-color: white;
|
||||
border-radius: 6px;
|
||||
border: 1px solid #EEEEEE;
|
||||
}
|
||||
|
||||
/* 标题样式 */
|
||||
QLabel#TitleLabel {
|
||||
font-family: "微软雅黑", "Segoe UI";
|
||||
font-size: 14pt;
|
||||
font-weight: bold;
|
||||
color: #212121;
|
||||
}
|
||||
|
||||
/* 副标题样式 */
|
||||
QLabel#SubtitleLabel, QTextEdit {
|
||||
font-family: "微软雅黑", "Segoe UI";
|
||||
font-size: 10pt;
|
||||
color: #666666;
|
||||
}
|
||||
|
||||
/* 滚动区域样式 */
|
||||
QScrollArea {
|
||||
border: none;
|
||||
}
|
||||
"""
|
||||
|
||||
# --------------------------- 下载线程 ---------------------------
|
||||
class DownloadThread(QThread):
|
||||
progress_updated = pyqtSignal(int, str, float) # (进度, 文件名, 下载速度MB/s)
|
||||
download_finished = pyqtSignal(bool, str, str) # (成功, 消息, 文件名)
|
||||
|
||||
def __init__(self, url, save_path, file_name, max_retries=2):
|
||||
super().__init__()
|
||||
self.url = url
|
||||
self.save_path = save_path
|
||||
self.file_name = file_name
|
||||
self.max_retries = max_retries # 最大重试次数
|
||||
self.start_time = None
|
||||
|
||||
def run(self):
|
||||
retry_count = 0
|
||||
while retry_count <= self.max_retries:
|
||||
try:
|
||||
# 确保保存目录存在(固定目录)
|
||||
save_dir = os.path.dirname(self.save_path)
|
||||
if not os.path.exists(save_dir):
|
||||
os.makedirs(save_dir)
|
||||
|
||||
# 发送请求(增加超时设置)
|
||||
self.start_time = time.time()
|
||||
response = requests.get(
|
||||
self.url,
|
||||
stream=True,
|
||||
timeout=15,
|
||||
headers={"User-Agent": "EasyUI-Installer/1.0"}
|
||||
)
|
||||
response.raise_for_status() # 触发HTTP错误
|
||||
total_size = int(response.headers.get('content-length', 0))
|
||||
|
||||
# 下载文件
|
||||
with open(self.save_path, 'wb') as file:
|
||||
downloaded_size = 0
|
||||
for data in response.iter_content(chunk_size=8192): # 增大缓冲区,提升速度
|
||||
if data:
|
||||
file.write(data)
|
||||
downloaded_size += len(data)
|
||||
# 计算进度和下载速度
|
||||
if total_size > 0:
|
||||
progress = int((downloaded_size / total_size) * 100)
|
||||
elapsed = time.time() - self.start_time
|
||||
speed = downloaded_size / (1024 * 1024 * elapsed) if elapsed > 0 else 0
|
||||
self.progress_updated.emit(progress, self.file_name, round(speed, 2))
|
||||
|
||||
# 验证文件完整性
|
||||
if os.path.exists(self.save_path) and os.path.getsize(self.save_path) > 0:
|
||||
self.download_finished.emit(True, self.save_path, self.file_name)
|
||||
else:
|
||||
raise Exception("文件下载不完整或为空")
|
||||
break # 成功则退出重试循环
|
||||
|
||||
except Exception as e:
|
||||
retry_count += 1
|
||||
if retry_count > self.max_retries:
|
||||
self.download_finished.emit(False, f"重试{self.max_retries}次后失败:{str(e)}", self.file_name)
|
||||
else:
|
||||
time.sleep(2) # 重试间隔2秒
|
||||
|
||||
|
||||
# --------------------------- 图标下载线程 ---------------------------
|
||||
class IconDownloadThread(QThread):
|
||||
download_finished = pyqtSignal(bool, QIcon, bytes) # (成功, 图标对象, 原始数据)
|
||||
|
||||
def __init__(self, icon_url):
|
||||
super().__init__()
|
||||
self.icon_url = icon_url
|
||||
|
||||
def run(self):
|
||||
try:
|
||||
# 下载图标
|
||||
response = requests.get(
|
||||
self.icon_url,
|
||||
timeout=10,
|
||||
headers={"User-Agent": "EasyUI-Installer/1.0"}
|
||||
)
|
||||
response.raise_for_status()
|
||||
|
||||
# 将下载的内容转换为QIcon
|
||||
image = QImage()
|
||||
if image.loadFromData(response.content):
|
||||
icon = QIcon(QPixmap.fromImage(image))
|
||||
self.download_finished.emit(True, icon, response.content)
|
||||
else:
|
||||
self.download_finished.emit(False, QIcon(), b'')
|
||||
|
||||
except Exception as e:
|
||||
print(f"图标下载失败: {str(e)}")
|
||||
self.download_finished.emit(False, QIcon(), b'')
|
||||
|
||||
|
||||
# --------------------------- 多文件下载管理器 ---------------------------
|
||||
class MultiFileDownloader(QThread):
|
||||
overall_progress = pyqtSignal(int) # 总体进度
|
||||
file_progress = pyqtSignal(int, str, float) # (单个进度, 文件名, 速度)
|
||||
file_finished = pyqtSignal(bool, str, str) # 单个文件完成
|
||||
all_finished = pyqtSignal() # 所有文件完成
|
||||
time_remaining = pyqtSignal(str) # 剩余时间(格式:mm:ss)
|
||||
|
||||
def __init__(self, downloads):
|
||||
super().__init__()
|
||||
self.downloads = downloads # [(url, save_path, file_name), ...]
|
||||
self.total_files = len(downloads)
|
||||
self.completed_files = 0
|
||||
self.current_file_index = 0
|
||||
self.current_thread = None
|
||||
self.stopped = False
|
||||
self.current_speed = 0 # 当前下载速度(MB/s)
|
||||
self.remaining_size = 0 # 剩余文件总大小(MB)
|
||||
|
||||
def run(self):
|
||||
# 预计算总大小(用于剩余时间估算)
|
||||
self.calc_total_remaining_size()
|
||||
|
||||
for i, (url, save_path, file_name) in enumerate(self.downloads):
|
||||
if self.stopped:
|
||||
break
|
||||
|
||||
self.current_file_index = i
|
||||
self.file_progress.emit(0, file_name, 0.0)
|
||||
|
||||
# 创建当前文件下载线程
|
||||
self.current_thread = DownloadThread(url, save_path, file_name)
|
||||
self.current_thread.progress_updated.connect(self.on_file_progress)
|
||||
self.current_thread.download_finished.connect(self.on_file_complete)
|
||||
self.current_thread.start()
|
||||
self.current_thread.wait()
|
||||
|
||||
self.all_finished.emit()
|
||||
|
||||
def calc_total_remaining_size(self):
|
||||
"""计算剩余文件总大小(MB)"""
|
||||
self.remaining_size = 0
|
||||
for url, save_path, file_name in self.downloads:
|
||||
try:
|
||||
response = requests.head(url, timeout=10)
|
||||
file_size = int(response.headers.get('content-length', 0)) / (1024 * 1024)
|
||||
self.remaining_size += file_size
|
||||
except:
|
||||
self.remaining_size += 10 # 估算未知大小文件为10MB
|
||||
|
||||
def on_file_progress(self, progress, file_name, speed):
|
||||
self.current_speed = speed
|
||||
self.file_progress.emit(progress, file_name, speed)
|
||||
|
||||
# 计算总体进度
|
||||
completed_size_ratio = (self.completed_files + progress/100) / self.total_files
|
||||
overall = int(completed_size_ratio * 100)
|
||||
self.overall_progress.emit(overall)
|
||||
|
||||
# 估算剩余时间(仅当速度>0时)
|
||||
if speed > 0.01:
|
||||
# 计算当前文件剩余大小 + 未开始文件大小
|
||||
current_file_remaining = (1 - progress/100) * (self.remaining_size / self.total_files)
|
||||
unstarted_files_size = (self.total_files - self.current_file_index - 1) * (self.remaining_size / self.total_files)
|
||||
total_remaining = current_file_remaining + unstarted_files_size
|
||||
|
||||
remaining_seconds = int(total_remaining / speed)
|
||||
minutes = remaining_seconds // 60
|
||||
seconds = remaining_seconds % 60
|
||||
self.time_remaining.emit(f"{minutes:02d}:{seconds:02d}")
|
||||
else:
|
||||
self.time_remaining.emit("--:--")
|
||||
|
||||
def on_file_complete(self, success, message, file_name):
|
||||
self.file_finished.emit(success, message, file_name)
|
||||
if success:
|
||||
self.completed_files += 1
|
||||
# 更新剩余大小
|
||||
self.remaining_size -= (self.remaining_size / self.total_files)
|
||||
|
||||
def stop(self):
|
||||
self.stopped = True
|
||||
if self.current_thread and self.current_thread.isRunning():
|
||||
self.current_thread.terminate()
|
||||
|
||||
|
||||
# --------------------------- 主安装窗口 ---------------------------
|
||||
class EasyUIInstaller(QMainWindow):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.setWindowTitle("Easy UI 安装程序")
|
||||
self.setGeometry(300, 200, 700, 500)
|
||||
self.setMinimumSize(700, 500)
|
||||
self.setStyleSheet(GLOBAL_STYLE)
|
||||
|
||||
# 图标配置 - 使用网络图标
|
||||
self.icon_url = "https://sunshinetown.oss-cn-shenzhen.aliyuncs.com/eui.ico" # 网络图标路径
|
||||
self.app_icon = QIcon() # 初始化空图标
|
||||
self.icon_data = b'' # 存储图标原始数据,用于创建快捷方式
|
||||
self.download_icon() # 启动图标下载
|
||||
|
||||
# 核心数据初始化 - 固定下载路径:C:\用户\自动识别用户名\JGZ_YES\EasyUILang\
|
||||
# 获取当前用户名
|
||||
self.user_name = os.path.expanduser("~").split(os.sep)[-1]
|
||||
# 构建固定路径
|
||||
self.fixed_install_dir = os.path.join("C:", "用户", self.user_name, "JGZ_YES", "EasyUILang")
|
||||
|
||||
self.resources = {
|
||||
"interpreter": {
|
||||
"name": "Easy UI 解释器",
|
||||
"url": "https://sunshinetown.oss-cn-shenzhen.aliyuncs.com/easy_ui_interpreter.exe",
|
||||
"default_path": os.path.join(self.fixed_install_dir, "easy_ui_interpreter.exe")
|
||||
},
|
||||
"editor": {
|
||||
"name": "Easy UI 编辑器",
|
||||
"url": "https://sunshinetown.oss-cn-shenzhen.aliyuncs.com/EasyUI_Editor.exe",
|
||||
"default_path": os.path.join(self.fixed_install_dir, "EasyUI_Editor.exe")
|
||||
}
|
||||
}
|
||||
self.download_queue = []
|
||||
self.download_manager = None
|
||||
|
||||
# 初始化分步容器
|
||||
self.init_stacked_widget()
|
||||
|
||||
def download_icon(self):
|
||||
"""下载网络图标"""
|
||||
print(f"正在下载图标: {self.icon_url}")
|
||||
self.icon_thread = IconDownloadThread(self.icon_url)
|
||||
self.icon_thread.download_finished.connect(self.on_icon_downloaded)
|
||||
self.icon_thread.start()
|
||||
|
||||
def on_icon_downloaded(self, success, icon, data):
|
||||
"""图标下载完成回调"""
|
||||
if success and not icon.isNull():
|
||||
print("图标下载成功")
|
||||
self.app_icon = icon
|
||||
self.icon_data = data # 保存图标原始数据
|
||||
self.setWindowIcon(self.app_icon)
|
||||
|
||||
# 更新界面上的图标
|
||||
if hasattr(self, 'logo_label'):
|
||||
pixmap = self.app_icon.pixmap(
|
||||
self.logo_label.width(),
|
||||
self.logo_label.height(),
|
||||
mode=QtGuiQIcon.Normal,
|
||||
state=QtGuiQIcon.On
|
||||
)
|
||||
self.logo_label.setPixmap(pixmap)
|
||||
|
||||
if hasattr(self, 'complete_icon'):
|
||||
pixmap = self.app_icon.pixmap(
|
||||
self.complete_icon.width(),
|
||||
self.complete_icon.height(),
|
||||
mode=QtGuiQIcon.Normal,
|
||||
state=QtGuiQIcon.On
|
||||
)
|
||||
self.complete_icon.setPixmap(pixmap)
|
||||
else:
|
||||
print("图标下载失败,使用默认图标")
|
||||
self.app_icon = QIcon.fromTheme("application-x-executable")
|
||||
self.setWindowIcon(self.app_icon)
|
||||
|
||||
def init_stacked_widget(self):
|
||||
"""创建分步容器"""
|
||||
self.central_widget = QWidget()
|
||||
self.setCentralWidget(self.central_widget)
|
||||
self.main_layout = QVBoxLayout(self.central_widget)
|
||||
self.main_layout.setContentsMargins(20, 20, 20, 20)
|
||||
self.main_layout.setSpacing(20)
|
||||
|
||||
# 1. 顶部Logo区域
|
||||
self.logo_layout = QHBoxLayout()
|
||||
self.logo_label = QLabel()
|
||||
self.logo_label.setFixedSize(40, 40) # 固定Logo大小
|
||||
|
||||
# 初始显示默认图标,下载完成后会更新
|
||||
if self.app_icon.isNull():
|
||||
palette = QPalette()
|
||||
palette.setColor(QPalette.Background, QColor("#1E88E5"))
|
||||
self.logo_label.setAutoFillBackground(True)
|
||||
self.logo_label.setPalette(palette)
|
||||
else:
|
||||
pixmap = self.app_icon.pixmap(
|
||||
self.logo_label.width(),
|
||||
self.logo_label.height()
|
||||
)
|
||||
self.logo_label.setPixmap(pixmap)
|
||||
|
||||
self.app_name_label = QLabel("Easy UI")
|
||||
self.app_name_label.setStyleSheet("font-size: 16pt; font-weight: bold; color: #1E88E5;")
|
||||
self.logo_layout.addWidget(self.logo_label)
|
||||
self.logo_layout.addWidget(self.app_name_label)
|
||||
self.logo_layout.addStretch()
|
||||
self.main_layout.addLayout(self.logo_layout)
|
||||
|
||||
# 2. 分步内容容器
|
||||
self.stacked_widget = QStackedWidget()
|
||||
self.main_layout.addWidget(self.stacked_widget, 1) # 占主要空间
|
||||
|
||||
# 3. 底部按钮区域
|
||||
self.btn_layout = QHBoxLayout()
|
||||
self.btn_layout.setSpacing(10)
|
||||
self.main_layout.addLayout(self.btn_layout)
|
||||
|
||||
# 初始化各页面
|
||||
self.init_welcome_page()
|
||||
self.init_license_page()
|
||||
self.init_component_page()
|
||||
self.init_progress_page()
|
||||
self.init_complete_page()
|
||||
|
||||
# 默认显示欢迎页
|
||||
self.stacked_widget.setCurrentIndex(0)
|
||||
self.update_buttons_by_page(0)
|
||||
|
||||
# --------------------------- 页面1:欢迎页 ---------------------------
|
||||
def init_welcome_page(self):
|
||||
self.welcome_page = QWidget()
|
||||
self.welcome_layout = QVBoxLayout(self.welcome_page)
|
||||
self.welcome_layout.setAlignment(Qt.AlignCenter)
|
||||
self.welcome_layout.setSpacing(30)
|
||||
|
||||
# 标题和副标题
|
||||
self.welcome_title = QLabel("欢迎使用 Easy UI 安装程序")
|
||||
self.welcome_title.setObjectName("TitleLabel")
|
||||
self.welcome_title.setAlignment(Qt.AlignCenter)
|
||||
|
||||
self.welcome_subtitle = QLabel("本程序将引导您完成 Easy UI 的安装,预计耗时2-5分钟")
|
||||
self.welcome_subtitle.setObjectName("SubtitleLabel")
|
||||
self.welcome_subtitle.setAlignment(Qt.AlignCenter)
|
||||
|
||||
# 系统要求提示
|
||||
self.system_require_label = QLabel(f"""
|
||||
系统要求:
|
||||
• Windows 10 及以上版本(64位)
|
||||
• 至少 100MB 可用磁盘空间
|
||||
• 稳定的网络连接(用于下载组件)
|
||||
|
||||
安装路径:
|
||||
C:\\用户\\{self.user_name}\\JGZ_YES\\EasyUILang\\
|
||||
""")
|
||||
self.system_require_label.setObjectName("SubtitleLabel")
|
||||
self.system_require_label.setAlignment(Qt.AlignLeft)
|
||||
|
||||
self.welcome_layout.addWidget(self.welcome_title)
|
||||
self.welcome_layout.addWidget(self.welcome_subtitle)
|
||||
self.welcome_layout.addWidget(self.system_require_label)
|
||||
self.stacked_widget.addWidget(self.welcome_page)
|
||||
|
||||
# --------------------------- 页面2:许可协议(可滚动版本) ---------------------------
|
||||
def init_license_page(self):
|
||||
self.license_page = QWidget()
|
||||
self.license_layout = QVBoxLayout(self.license_page)
|
||||
self.license_layout.setSpacing(20)
|
||||
|
||||
# 标题
|
||||
self.license_title = QLabel("软件许可协议")
|
||||
self.license_title.setObjectName("TitleLabel")
|
||||
self.license_layout.addWidget(self.license_title)
|
||||
|
||||
# 协议内容面板(带滚动功能)
|
||||
self.license_frame = QFrame()
|
||||
self.license_frame.setObjectName("CardFrame")
|
||||
self.license_frame.setFixedHeight(250)
|
||||
self.license_inner_layout = QVBoxLayout(self.license_frame)
|
||||
|
||||
# 添加滚动区域
|
||||
self.license_scroll = QScrollArea()
|
||||
self.license_scroll.setWidgetResizable(True)
|
||||
self.license_scroll_content = QWidget()
|
||||
self.license_scroll_layout = QVBoxLayout(self.license_scroll_content)
|
||||
|
||||
# 协议文本
|
||||
self.license_text = QTextEdit()
|
||||
self.license_text.setReadOnly(True)
|
||||
self.license_text.setText("""
|
||||
Easy UI 软件许可协议
|
||||
|
||||
1. 许可范围
|
||||
本软件仅供个人非商业使用,禁止未经授权用于商业用途、盈利活动或非法行为。
|
||||
|
||||
2. 用户权利
|
||||
• 免费使用软件的全部功能及更新服务
|
||||
• 在许可范围内修改个人使用的软件副本
|
||||
• 获得软件相关的技术支持(限非商业用途)
|
||||
|
||||
3. 用户义务
|
||||
• 不得传播经过篡改、破解或植入恶意代码的软件版本
|
||||
• 不得利用软件侵犯他人知识产权、隐私或其他合法权益
|
||||
• 不得违反国家法律法规及相关政策使用软件
|
||||
|
||||
4. 免责声明
|
||||
• 软件按“现状”提供,开发者不保证无故障运行或完全满足用户需求
|
||||
• 开发者不对软件使用过程中产生的任何直接或间接损失承担责任
|
||||
• 因用户违反本协议导致的法律风险,由用户自行承担
|
||||
|
||||
5. 协议生效与终止
|
||||
• 用户点击“同意”即表示完全接受本协议所有条款
|
||||
• 若用户违反本协议,开发者有权终止其使用许可
|
||||
""")
|
||||
self.license_scroll_layout.addWidget(self.license_text)
|
||||
self.license_scroll.setWidget(self.license_scroll_content)
|
||||
|
||||
self.license_inner_layout.addWidget(self.license_scroll)
|
||||
self.license_layout.addWidget(self.license_frame)
|
||||
|
||||
# 同意勾选框
|
||||
self.agree_check = QCheckBox("我已阅读并同意上述许可协议")
|
||||
self.agree_check.setObjectName("SubtitleLabel")
|
||||
self.agree_check.setChecked(False)
|
||||
self.license_layout.addWidget(self.agree_check)
|
||||
|
||||
self.license_layout.addStretch()
|
||||
self.stacked_widget.addWidget(self.license_page)
|
||||
|
||||
# --------------------------- 页面3:组件选择 ---------------------------
|
||||
def init_component_page(self):
|
||||
self.component_page = QWidget()
|
||||
self.component_layout = QVBoxLayout(self.component_page)
|
||||
self.component_layout.setSpacing(20)
|
||||
|
||||
# 标题和副标题
|
||||
self.component_title = QLabel("选择安装组件")
|
||||
self.component_title.setObjectName("TitleLabel")
|
||||
self.component_layout.addWidget(self.component_title)
|
||||
|
||||
self.component_subtitle = QLabel("默认安装所有组件,您可根据需求取消不需要的选项")
|
||||
self.component_subtitle.setObjectName("SubtitleLabel")
|
||||
self.component_layout.addWidget(self.component_subtitle)
|
||||
|
||||
# 组件选择面板
|
||||
self.component_frame = QFrame()
|
||||
self.component_frame.setObjectName("CardFrame")
|
||||
self.component_inner_layout = QVBoxLayout(self.component_frame)
|
||||
self.component_inner_layout.setContentsMargins(20, 20, 20, 20)
|
||||
self.component_inner_layout.setSpacing(25)
|
||||
|
||||
# 解释器组件
|
||||
self.interpreter_group = QWidget()
|
||||
self.interpreter_group_layout = QVBoxLayout(self.interpreter_group)
|
||||
self.interpreter_group_layout.setSpacing(10)
|
||||
|
||||
self.interpreter_check = QCheckBox(f"{self.resources['interpreter']['name']} (必需组件)")
|
||||
self.interpreter_check.setObjectName("SubtitleLabel")
|
||||
self.interpreter_check.setChecked(True)
|
||||
self.interpreter_check.setEnabled(False)
|
||||
self.interpreter_group_layout.addWidget(self.interpreter_check)
|
||||
|
||||
# 解释器路径选择(固定路径,不可修改)
|
||||
self.interpreter_path_layout = QHBoxLayout()
|
||||
self.interpreter_path_label = QLabel("安装位置:")
|
||||
self.interpreter_path_label.setObjectName("SubtitleLabel")
|
||||
self.interpreter_path = QLineEdit(self.resources['interpreter']['default_path'])
|
||||
self.interpreter_path.setReadOnly(True) # 设置为只读,防止修改
|
||||
self.interpreter_browse_btn = QPushButton("浏览...")
|
||||
self.interpreter_browse_btn.setObjectName("SecondaryBtn")
|
||||
self.interpreter_browse_btn.clicked.connect(lambda: self.show_path_info())
|
||||
|
||||
self.interpreter_path_layout.addWidget(self.interpreter_path_label)
|
||||
self.interpreter_path_layout.addWidget(self.interpreter_path, 1)
|
||||
self.interpreter_path_layout.addWidget(self.interpreter_browse_btn)
|
||||
self.interpreter_group_layout.addLayout(self.interpreter_path_layout)
|
||||
self.component_inner_layout.addWidget(self.interpreter_group)
|
||||
|
||||
# 编辑器组件
|
||||
self.editor_group = QWidget()
|
||||
self.editor_group_layout = QVBoxLayout(self.editor_group)
|
||||
self.editor_group_layout.setSpacing(10)
|
||||
|
||||
self.editor_check = QCheckBox(f"{self.resources['editor']['name']} (推荐组件)")
|
||||
self.editor_check.setObjectName("SubtitleLabel")
|
||||
self.editor_check.setChecked(True)
|
||||
self.editor_group_layout.addWidget(self.editor_check)
|
||||
|
||||
# 编辑器路径选择(固定路径,不可修改)
|
||||
self.editor_path_layout = QHBoxLayout()
|
||||
self.editor_path_label = QLabel("安装位置:")
|
||||
self.editor_path_label.setObjectName("SubtitleLabel")
|
||||
self.editor_path = QLineEdit(self.resources['editor']['default_path'])
|
||||
self.editor_path.setReadOnly(True) # 设置为只读,防止修改
|
||||
self.editor_browse_btn = QPushButton("浏览...")
|
||||
self.editor_browse_btn.setObjectName("SecondaryBtn")
|
||||
self.editor_browse_btn.clicked.connect(lambda: self.show_path_info())
|
||||
|
||||
self.editor_path_layout.addWidget(self.editor_path_label)
|
||||
self.editor_path_layout.addWidget(self.editor_path, 1)
|
||||
self.editor_path_layout.addWidget(self.editor_browse_btn)
|
||||
self.editor_group_layout.addLayout(self.editor_path_layout)
|
||||
self.component_inner_layout.addWidget(self.editor_group)
|
||||
|
||||
self.component_layout.addWidget(self.component_frame)
|
||||
self.stacked_widget.addWidget(self.component_page)
|
||||
|
||||
def show_path_info(self):
|
||||
"""显示路径信息提示,告知用户路径不可修改"""
|
||||
QMessageBox.information(
|
||||
self, "安装路径说明",
|
||||
f"软件将固定安装到以下路径:\nC:\\用户\\{self.user_name}\\JGZ_YES\\EasyUILang\\"
|
||||
)
|
||||
|
||||
# --------------------------- 页面4:安装进度 ---------------------------
|
||||
def init_progress_page(self):
|
||||
self.progress_page = QWidget()
|
||||
self.progress_layout = QVBoxLayout(self.progress_page)
|
||||
self.progress_layout.setSpacing(20)
|
||||
|
||||
# 标题
|
||||
self.progress_title = QLabel("正在安装")
|
||||
self.progress_title.setObjectName("TitleLabel")
|
||||
self.progress_layout.addWidget(self.progress_title)
|
||||
|
||||
# 进度面板
|
||||
self.progress_frame = QFrame()
|
||||
self.progress_frame.setObjectName("CardFrame")
|
||||
self.progress_inner_layout = QVBoxLayout(self.progress_frame)
|
||||
self.progress_inner_layout.setContentsMargins(20, 20, 20, 20)
|
||||
self.progress_inner_layout.setSpacing(15)
|
||||
|
||||
# 当前任务提示
|
||||
self.current_task_label = QLabel("准备下载安装组件...")
|
||||
self.current_task_label.setObjectName("SubtitleLabel")
|
||||
self.progress_inner_layout.addWidget(self.current_task_label)
|
||||
|
||||
# 总体进度条
|
||||
self.overall_progress_bar = QProgressBar()
|
||||
self.overall_progress_bar.setValue(0)
|
||||
self.progress_inner_layout.addWidget(self.overall_progress_bar)
|
||||
|
||||
# 进度详情
|
||||
self.progress_detail_layout = QHBoxLayout()
|
||||
self.speed_label = QLabel("速度:0.00 MB/s")
|
||||
self.speed_label.setObjectName("SubtitleLabel")
|
||||
self.time_remaining_label = QLabel("剩余时间:--:--")
|
||||
self.time_remaining_label.setObjectName("SubtitleLabel")
|
||||
self.progress_detail_layout.addWidget(self.speed_label)
|
||||
self.progress_detail_layout.addStretch()
|
||||
self.progress_detail_layout.addWidget(self.time_remaining_label)
|
||||
self.progress_inner_layout.addLayout(self.progress_detail_layout)
|
||||
|
||||
self.progress_layout.addWidget(self.progress_frame)
|
||||
self.stacked_widget.addWidget(self.progress_page)
|
||||
|
||||
# --------------------------- 页面5:安装完成 ---------------------------
|
||||
def init_complete_page(self):
|
||||
self.complete_page = QWidget()
|
||||
self.complete_layout = QVBoxLayout(self.complete_page)
|
||||
self.complete_layout.setSpacing(25)
|
||||
self.complete_layout.setAlignment(Qt.AlignCenter)
|
||||
|
||||
# 完成图标
|
||||
self.complete_icon = QLabel()
|
||||
self.complete_icon.setFixedSize(80, 80)
|
||||
|
||||
# 初始显示默认图标
|
||||
if self.app_icon.isNull():
|
||||
palette = QPalette()
|
||||
palette.setColor(QPalette.Background, QColor("#4CAF50"))
|
||||
self.complete_icon.setAutoFillBackground(True)
|
||||
self.complete_icon.setPalette(palette)
|
||||
self.complete_icon.setStyleSheet("border-radius: 40px;")
|
||||
else:
|
||||
pixmap = self.app_icon.pixmap(
|
||||
self.complete_icon.width(),
|
||||
self.complete_icon.height()
|
||||
)
|
||||
self.complete_icon.setPixmap(pixmap)
|
||||
|
||||
self.complete_layout.addWidget(self.complete_icon, alignment=Qt.AlignCenter)
|
||||
|
||||
# 完成标题
|
||||
self.complete_title = QLabel("安装完成!")
|
||||
self.complete_title.setObjectName("TitleLabel")
|
||||
self.complete_layout.addWidget(self.complete_title, alignment=Qt.AlignCenter)
|
||||
|
||||
# 完成提示
|
||||
self.complete_subtitle = QLabel(f"Easy UI 已成功安装到:\nC:\\用户\\{self.user_name}\\JGZ_YES\\EasyUILang\\")
|
||||
self.complete_subtitle.setObjectName("SubtitleLabel")
|
||||
self.complete_subtitle.setAlignment(Qt.AlignCenter)
|
||||
self.complete_layout.addWidget(self.complete_subtitle, alignment=Qt.AlignCenter)
|
||||
|
||||
# 后续操作选项
|
||||
self.complete_options_layout = QVBoxLayout()
|
||||
self.run_check = QCheckBox("立即运行 Easy UI 编辑器")
|
||||
self.run_check.setObjectName("SubtitleLabel")
|
||||
self.run_check.setChecked(True)
|
||||
|
||||
self.desktop_shortcut_check = QCheckBox("创建桌面快捷方式")
|
||||
self.desktop_shortcut_check.setObjectName("SubtitleLabel")
|
||||
self.desktop_shortcut_check.setChecked(True)
|
||||
|
||||
self.complete_options_layout.addWidget(self.run_check)
|
||||
self.complete_options_layout.addWidget(self.desktop_shortcut_check)
|
||||
self.complete_layout.addLayout(self.complete_options_layout)
|
||||
|
||||
self.stacked_widget.addWidget(self.complete_page)
|
||||
|
||||
# --------------------------- 核心交互逻辑 ---------------------------
|
||||
def update_buttons_by_page(self, page_index):
|
||||
"""根据当前页面更新底部按钮"""
|
||||
# 清空现有按钮
|
||||
for i in range(self.btn_layout.count()):
|
||||
widget = self.btn_layout.itemAt(i).widget()
|
||||
if widget:
|
||||
widget.deleteLater()
|
||||
|
||||
# 根据页面索引添加按钮
|
||||
if page_index == 0: # 欢迎页:仅“下一步”
|
||||
self.next_btn = QPushButton("下一步")
|
||||
self.next_btn.setObjectName("PrimaryBtn")
|
||||
self.next_btn.clicked.connect(lambda: self.switch_page(1))
|
||||
self.btn_layout.addStretch()
|
||||
self.btn_layout.addWidget(self.next_btn)
|
||||
|
||||
elif page_index == 1: # 许可协议:“上一步”+“下一步”
|
||||
self.prev_btn = QPushButton("上一步")
|
||||
self.prev_btn.setObjectName("SecondaryBtn")
|
||||
self.prev_btn.clicked.connect(lambda: self.switch_page(0))
|
||||
|
||||
self.next_btn = QPushButton("下一步")
|
||||
self.next_btn.setObjectName("PrimaryBtn")
|
||||
self.next_btn.setEnabled(False)
|
||||
self.next_btn.clicked.connect(lambda: self.switch_page(2))
|
||||
|
||||
# 勾选框联动按钮状态
|
||||
self.agree_check.stateChanged.connect(
|
||||
lambda state: self.next_btn.setEnabled(state == Qt.Checked)
|
||||
)
|
||||
|
||||
self.btn_layout.addWidget(self.prev_btn)
|
||||
self.btn_layout.addStretch()
|
||||
self.btn_layout.addWidget(self.next_btn)
|
||||
|
||||
elif page_index == 2: # 组件选择:“上一步”+“安装”
|
||||
self.prev_btn = QPushButton("上一步")
|
||||
self.prev_btn.setObjectName("SecondaryBtn")
|
||||
self.prev_btn.clicked.connect(lambda: self.switch_page(1))
|
||||
|
||||
self.install_btn = QPushButton("开始安装")
|
||||
self.install_btn.setObjectName("PrimaryBtn")
|
||||
self.install_btn.clicked.connect(self.start_install)
|
||||
|
||||
self.btn_layout.addWidget(self.prev_btn)
|
||||
self.btn_layout.addStretch()
|
||||
self.btn_layout.addWidget(self.install_btn)
|
||||
|
||||
elif page_index == 3: # 安装进度:“取消”
|
||||
self.cancel_btn = QPushButton("取消安装")
|
||||
self.cancel_btn.setObjectName("SecondaryBtn")
|
||||
self.cancel_btn.clicked.connect(self.cancel_install)
|
||||
self.btn_layout.addStretch()
|
||||
self.btn_layout.addWidget(self.cancel_btn)
|
||||
|
||||
elif page_index == 4: # 安装完成:“完成”
|
||||
self.finish_btn = QPushButton("完成")
|
||||
self.finish_btn.setObjectName("PrimaryBtn")
|
||||
self.finish_btn.clicked.connect(self.finish_install)
|
||||
self.btn_layout.addStretch()
|
||||
self.btn_layout.addWidget(self.finish_btn)
|
||||
|
||||
def switch_page(self, target_index):
|
||||
"""切换页面并更新按钮"""
|
||||
self.stacked_widget.setCurrentIndex(target_index)
|
||||
self.update_buttons_by_page(target_index)
|
||||
|
||||
def start_install(self):
|
||||
"""验证组件并开始安装"""
|
||||
# 构建下载队列(固定路径)
|
||||
self.download_queue = []
|
||||
if self.interpreter_check.isChecked():
|
||||
inter_path = self.resources['interpreter']['default_path']
|
||||
self.download_queue.append((
|
||||
self.resources['interpreter']['url'],
|
||||
inter_path,
|
||||
self.resources['interpreter']['name']
|
||||
))
|
||||
|
||||
if self.editor_check.isChecked():
|
||||
edit_path = self.resources['editor']['default_path']
|
||||
self.download_queue.append((
|
||||
self.resources['editor']['url'],
|
||||
edit_path,
|
||||
self.resources['editor']['name']
|
||||
))
|
||||
|
||||
# 验证路径权限
|
||||
if not self.verify_path_permission():
|
||||
return
|
||||
|
||||
# 跳转到进度页并启动下载
|
||||
self.switch_page(3)
|
||||
self.download_manager = MultiFileDownloader(self.download_queue)
|
||||
self.download_manager.overall_progress.connect(self.overall_progress_bar.setValue)
|
||||
self.download_manager.file_progress.connect(self.update_progress_detail)
|
||||
self.download_manager.time_remaining.connect(self.time_remaining_label.setText)
|
||||
self.download_manager.file_finished.connect(self.on_file_finished)
|
||||
self.download_manager.all_finished.connect(self.on_all_finished)
|
||||
self.download_manager.start()
|
||||
|
||||
def verify_path_permission(self):
|
||||
"""验证安装路径是否有写入权限"""
|
||||
for url, path, name in self.download_queue:
|
||||
try:
|
||||
test_dir = os.path.dirname(path)
|
||||
if not os.path.exists(test_dir):
|
||||
os.makedirs(test_dir)
|
||||
|
||||
# 测试写入
|
||||
test_file = os.path.join(test_dir, "test_permission.tmp")
|
||||
with open(test_file, 'w') as f:
|
||||
f.write("test")
|
||||
os.remove(test_file)
|
||||
except Exception as e:
|
||||
QMessageBox.warning(
|
||||
self, "路径权限错误",
|
||||
f"无法写入{name}的安装路径:\n{str(e)}\n请检查路径权限或以管理员身份运行安装程序。"
|
||||
)
|
||||
return False
|
||||
return True
|
||||
|
||||
def update_progress_detail(self, progress, file_name, speed):
|
||||
"""更新进度详情"""
|
||||
self.current_task_label.setText(f"正在安装 {file_name}... ({progress}%)")
|
||||
self.speed_label.setText(f"速度:{speed:.2f} MB/s")
|
||||
|
||||
def on_file_finished(self, success, message, file_name):
|
||||
"""单个文件安装完成回调"""
|
||||
if not success:
|
||||
QMessageBox.warning(self, f"{file_name}安装失败", message)
|
||||
|
||||
def on_all_finished(self):
|
||||
"""所有文件安装完成"""
|
||||
# 创建桌面快捷方式(如果勾选)
|
||||
if self.desktop_shortcut_check.isChecked():
|
||||
self.create_desktop_shortcut()
|
||||
self.switch_page(4)
|
||||
|
||||
def create_desktop_shortcut(self):
|
||||
"""创建桌面快捷方式"""
|
||||
editor_path = self.resources['editor']['default_path']
|
||||
if not os.path.exists(editor_path):
|
||||
QMessageBox.warning(self, "创建失败", "未找到Easy UI编辑器可执行文件,无法创建快捷方式")
|
||||
return
|
||||
|
||||
# 尝试创建快捷方式
|
||||
try:
|
||||
import pythoncom
|
||||
import win32com.client
|
||||
from winshell import desktop
|
||||
|
||||
# 初始化COM
|
||||
pythoncom.CoInitialize()
|
||||
|
||||
# 获取桌面路径
|
||||
desktop_path = desktop()
|
||||
shortcut_path = os.path.join(desktop_path, "Easy UI 编辑器.lnk")
|
||||
|
||||
# 保存图标到本地临时文件
|
||||
icon_temp_path = os.path.join(self.fixed_install_dir, "eui_icon.ico")
|
||||
if self.icon_data:
|
||||
with open(icon_temp_path, 'wb') as f:
|
||||
f.write(self.icon_data)
|
||||
|
||||
# 创建快捷方式
|
||||
shell = win32com.client.Dispatch("WScript.Shell")
|
||||
shortcut = shell.CreateShortCut(shortcut_path)
|
||||
shortcut.TargetPath = editor_path
|
||||
shortcut.WorkingDirectory = self.fixed_install_dir
|
||||
shortcut.Description = "Easy UI 编辑器"
|
||||
shortcut.Hotkey = "Ctrl+Alt+E"
|
||||
|
||||
# 设置图标
|
||||
if os.path.exists(icon_temp_path):
|
||||
shortcut.IconLocation = icon_temp_path
|
||||
else:
|
||||
shortcut.IconLocation = editor_path
|
||||
|
||||
shortcut.save()
|
||||
pythoncom.CoUninitialize()
|
||||
return
|
||||
|
||||
except ImportError:
|
||||
# 备用方案:使用PowerShell命令
|
||||
try:
|
||||
import subprocess
|
||||
|
||||
# 获取桌面路径
|
||||
desktop_path = os.path.join(os.path.expanduser("~"), "Desktop")
|
||||
shortcut_path = os.path.join(desktop_path, "Easy UI 编辑器.lnk")
|
||||
|
||||
# 保存图标
|
||||
icon_temp_path = os.path.join(self.fixed_install_dir, "eui_icon.ico")
|
||||
if self.icon_data:
|
||||
with open(icon_temp_path, 'wb') as f:
|
||||
f.write(self.icon_data)
|
||||
|
||||
# PowerShell命令
|
||||
ps_command = f'''
|
||||
$WshShell = New-Object -ComObject WScript.Shell
|
||||
$shortcut = $WshShell.CreateShortcut("{shortcut_path}")
|
||||
$shortcut.TargetPath = "{editor_path}"
|
||||
$shortcut.WorkingDirectory = "{self.fixed_install_dir}"
|
||||
$shortcut.Description = "Easy UI 编辑器"
|
||||
{f'$shortcut.IconLocation = "{icon_temp_path}"' if os.path.exists(icon_temp_path) else ''}
|
||||
$shortcut.Save()
|
||||
'''
|
||||
|
||||
subprocess.run(
|
||||
["powershell", "-Command", ps_command],
|
||||
check=True,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE,
|
||||
text=True
|
||||
)
|
||||
return
|
||||
|
||||
except Exception as e:
|
||||
print(f"创建快捷方式失败:{str(e)}")
|
||||
QMessageBox.warning(
|
||||
self, "创建失败",
|
||||
"无法自动创建桌面快捷方式,您可以手动创建:\n"
|
||||
f"1. 找到文件:{editor_path}\n"
|
||||
"2. 右键点击文件,选择'发送到'->'桌面快捷方式'"
|
||||
)
|
||||
|
||||
def cancel_install(self):
|
||||
"""取消安装"""
|
||||
if self.download_manager and self.download_manager.isRunning():
|
||||
confirm = QMessageBox.question(
|
||||
self, "确认取消",
|
||||
"取消安装将终止当前下载,已下载的文件可能不完整,是否继续?",
|
||||
QMessageBox.Yes | QMessageBox.No
|
||||
)
|
||||
if confirm == QMessageBox.Yes:
|
||||
self.download_manager.stop()
|
||||
self.switch_page(2) # 回到组件选择页
|
||||
|
||||
def finish_install(self):
|
||||
"""完成安装"""
|
||||
# 运行程序(如果勾选)
|
||||
if self.run_check.isChecked():
|
||||
editor_path = self.resources['editor']['default_path']
|
||||
if os.path.exists(editor_path):
|
||||
os.startfile(editor_path)
|
||||
# 关闭安装程序
|
||||
self.close()
|
||||
|
||||
|
||||
# 程序入口
|
||||
if __name__ == "__main__":
|
||||
# 先设置高DPI属性
|
||||
if hasattr(Qt, 'AA_EnableHighDpiScaling'):
|
||||
QApplication.setAttribute(Qt.AA_EnableHighDpiScaling, True)
|
||||
if hasattr(Qt, 'AA_UseHighDpiPixmaps'):
|
||||
QApplication.setAttribute(Qt.AA_UseHighDpiPixmaps, True)
|
||||
|
||||
# 确保中文显示正常
|
||||
app = QApplication(sys.argv)
|
||||
app.setFont(QFont("微软雅黑", 9))
|
||||
|
||||
window = EasyUIInstaller()
|
||||
window.show()
|
||||
|
||||
sys.exit(app.exec_())
|
||||
2056
easy_ui_editor.py
Normal file
2056
easy_ui_editor.py
Normal file
File diff suppressed because it is too large
Load Diff
686
easy_ui_interpreter.py
Normal file
686
easy_ui_interpreter.py
Normal file
@@ -0,0 +1,686 @@
|
||||
import sys
|
||||
import os
|
||||
import re
|
||||
from PyQt5.QtWidgets import (QApplication, QMainWindow, QLabel, QLineEdit,
|
||||
QComboBox, QCheckBox, QPushButton, QWidget,
|
||||
QVBoxLayout, QHBoxLayout, QMessageBox, QFrame,
|
||||
QTextEdit, QSlider, QProgressBar, QCalendarWidget,
|
||||
QGroupBox, QRadioButton)
|
||||
from PyQt5.QtCore import Qt, QUrl, QTimer, pyqtSlot
|
||||
from PyQt5.QtGui import QIcon, QIntValidator, QPixmap, QImage
|
||||
from PyQt5.QtMultimedia import QMediaPlayer, QMediaContent
|
||||
from urllib.request import urlopen
|
||||
|
||||
class EasyUIInterpreter:
|
||||
def __init__(self):
|
||||
self.app = None
|
||||
self.window = None
|
||||
self.widgets = {} # 存储所有组件
|
||||
self.variables = {} # 存储可交互组件
|
||||
self.main_layout = None
|
||||
self.media_players = {} # 仅保留音频播放器
|
||||
self.timers = {} # 存储定时器
|
||||
self.groups = {}
|
||||
|
||||
def parse_and_run(self, code):
|
||||
if not QApplication.instance():
|
||||
self.app = QApplication(sys.argv)
|
||||
else:
|
||||
self.app = QApplication.instance()
|
||||
|
||||
# 重置UI状态
|
||||
self.widgets = {}
|
||||
self.variables = {}
|
||||
self.media_players = {}
|
||||
self.timers = {}
|
||||
self.groups = {}
|
||||
self.window = None
|
||||
self.main_layout = None
|
||||
|
||||
lines = [line.strip() for line in code.split('\n') if line.strip()]
|
||||
for line in lines:
|
||||
self.parse_line(line)
|
||||
|
||||
if not self.window:
|
||||
self.create_window("EUI默认窗口", 400, 300)
|
||||
else:
|
||||
self.main_layout.addStretch()
|
||||
|
||||
self.window.show()
|
||||
sys.exit(self.app.exec_())
|
||||
|
||||
# ---------------------- 解析逻辑 ----------------------
|
||||
def parse_line(self, line):
|
||||
line = line.strip().rstrip(';')
|
||||
if not line:
|
||||
return
|
||||
|
||||
# 窗口配置
|
||||
window_pattern = r'window\s*=\s*title="([^"]+)"\s*,\s*width=(\d+)\s*,\s*height=(\d+)(?:\s*,\s*icon="([^"]+)")?'
|
||||
window_match = re.match(window_pattern, line)
|
||||
if window_match:
|
||||
title = window_match.group(1)
|
||||
width = int(window_match.group(2))
|
||||
height = int(window_match.group(3))
|
||||
icon_path = window_match.group(4) if window_match.group(4) else None
|
||||
self.create_window(title, width, height, icon_path)
|
||||
return
|
||||
|
||||
# 文字标签
|
||||
label_match = re.match(r'label\s*=\s*text="([^"]+)"\s*,\s*id=(\w+)', line)
|
||||
if label_match:
|
||||
self.create_label(label_match.group(1), label_match.group(2))
|
||||
return
|
||||
|
||||
# 输入框
|
||||
entry_pattern = r'entry\s*=\s*hint="([^"]+)"\s*,\s*id=(\w+)(?:\s*,\s*readonly=(true|false))?(?:\s*,\s*type=(number|text))?'
|
||||
entry_match = re.match(entry_pattern, line)
|
||||
if entry_match:
|
||||
hint = entry_match.group(1)
|
||||
widget_id = entry_match.group(2)
|
||||
readonly = entry_match.group(3).lower() == 'true' if entry_match.group(3) else False
|
||||
input_type = entry_match.group(4) if entry_match.group(4) else 'text'
|
||||
self.create_entry(hint, widget_id, readonly, input_type)
|
||||
return
|
||||
|
||||
# 下拉选择框
|
||||
combo_match = re.match(r'combo\s*=\s*label="([^"]+)"\s*,\s*id=(\w+)\s*,\s*options=\[(.*?)\]', line)
|
||||
if combo_match:
|
||||
options = [opt.strip().strip('"') for opt in combo_match.group(3).split(',') if opt.strip()]
|
||||
self.create_combobox(combo_match.group(1), combo_match.group(2), options)
|
||||
return
|
||||
|
||||
# 多选框组
|
||||
check_match = re.match(r'checkbox\s*=\s*label="([^"]+)"\s*,\s*id=(\w+)\s*,\s*options=\[(.*?)\]', line)
|
||||
if check_match:
|
||||
options = [opt.strip().strip('"') for opt in check_match.group(3).split(',') if opt.strip()]
|
||||
self.create_checkboxes(check_match.group(1), check_match.group(2), options)
|
||||
return
|
||||
|
||||
# 按钮
|
||||
button_match = re.match(r'button\s*=\s*text="([^"]+)"\s*,\s*id=(\w+)\s*,\s*click="([^"]+)"', line)
|
||||
if button_match:
|
||||
self.create_button(button_match.group(1), button_match.group(2), button_match.group(3))
|
||||
return
|
||||
|
||||
# 音频播放器
|
||||
audio_pattern = r'audio\s*=\s*(url|os)="([^"]+)"\s*,\s*id=(\w+)'
|
||||
audio_match = re.match(audio_pattern, line)
|
||||
if audio_match:
|
||||
self.create_audio_player(audio_match.group(1), audio_match.group(2), audio_match.group(3))
|
||||
return
|
||||
|
||||
# 图片组件
|
||||
image_pattern = r'image\s*=\s*(path|url|os)="([^"]+)"\s*,\s*id=(\w+)(?:\s*,\s*width=(\d+))?(?:\s*,\s*height=(\d+))?(?:\s*,\s*tooltip="([^"]+)")?'
|
||||
image_match = re.match(image_pattern, line)
|
||||
if image_match:
|
||||
img_type = image_match.group(1)
|
||||
img_path = image_match.group(2)
|
||||
img_id = image_match.group(3)
|
||||
width = int(image_match.group(4)) if image_match.group(4) else None
|
||||
height = int(image_match.group(5)) if image_match.group(5) else None
|
||||
tooltip = image_match.group(6) if image_match.group(6) else ""
|
||||
self.create_image(img_type, img_path, img_id, width, height, tooltip)
|
||||
return
|
||||
|
||||
# 滑块控件
|
||||
slider_pattern = r'slider\s*=\s*label="([^"]+)"\s*,\s*id=(\w+)\s*,\s*min=(\d+)\s*,\s*max=(\d+)\s*,\s*value=(\d+)'
|
||||
slider_match = re.match(slider_pattern, line)
|
||||
if slider_match:
|
||||
self.create_slider(
|
||||
slider_match.group(1), slider_match.group(2),
|
||||
int(slider_match.group(3)), int(slider_match.group(4)), int(slider_match.group(5))
|
||||
)
|
||||
return
|
||||
|
||||
# 文本区域
|
||||
textarea_pattern = r'textarea\s*=\s*label="([^"]+)"\s*,\s*id=(\w+)\s*,\s*rows=(\d+)(?:\s*,\s*readonly=(true|false))?'
|
||||
textarea_match = re.match(textarea_pattern, line)
|
||||
if textarea_match:
|
||||
readonly = textarea_match.group(4).lower() == 'true' if textarea_match.group(4) else False
|
||||
self.create_textarea(textarea_match.group(1), textarea_match.group(2), int(textarea_match.group(3)), readonly)
|
||||
return
|
||||
|
||||
# 分隔线
|
||||
separator_match = re.match(r'separator\s*=\s*text="([^"]*)"\s*,\s*id=(\w+)', line)
|
||||
if separator_match:
|
||||
self.create_separator(separator_match.group(1), separator_match.group(2))
|
||||
return
|
||||
|
||||
# 进度条
|
||||
progress_pattern = r'progress\s*=\s*label="([^"]+)"\s*,\s*id=(\w+)\s*,\s*min=(\d+)\s*,\s*max=(\d+)\s*,\s*value=(\d+)'
|
||||
progress_match = re.match(progress_pattern, line)
|
||||
if progress_match:
|
||||
self.create_progressbar(
|
||||
progress_match.group(1), progress_match.group(2),
|
||||
int(progress_match.group(3)), int(progress_match.group(4)), int(progress_match.group(5))
|
||||
)
|
||||
return
|
||||
|
||||
# 日历控件
|
||||
calendar_match = re.match(r'calendar\s*=\s*label="([^"]+)"\s*,\s*id=(\w+)', line)
|
||||
if calendar_match:
|
||||
self.create_calendar(calendar_match.group(1), calendar_match.group(2))
|
||||
return
|
||||
|
||||
# 单选按钮组
|
||||
radio_match = re.match(r'radiogroup\s*=\s*label="([^"]+)"\s*,\s*id=(\w+)\s*,\s*options=\[(.*?)\]', line)
|
||||
if radio_match:
|
||||
options = [opt.strip().strip('"') for opt in radio_match.group(3).split(',') if opt.strip()]
|
||||
self.create_radiogroup(radio_match.group(1), radio_match.group(2), options)
|
||||
return
|
||||
|
||||
# 分组框
|
||||
groupbox_match = re.match(r'groupbox\s*=\s*title="([^"]+)"\s*,\s*id=(\w+)', line)
|
||||
if groupbox_match:
|
||||
self.create_groupbox(groupbox_match.group(1), groupbox_match.group(2))
|
||||
return
|
||||
|
||||
# 定时器
|
||||
timer_pattern = r'timer\s*=\s*id=(\w+)\s*,\s*interval=(\d+)\s*,\s*action="([^"]+)"'
|
||||
timer_match = re.match(timer_pattern, line)
|
||||
if timer_match:
|
||||
self.create_timer(timer_match.group(1), int(timer_match.group(2)), timer_match.group(3))
|
||||
return
|
||||
|
||||
# ---------------------- 组件创建方法 ----------------------
|
||||
def create_window(self, title, width, height, icon_path=None):
|
||||
self.window = QMainWindow()
|
||||
self.window.setWindowTitle(title)
|
||||
self.window.resize(width, height)
|
||||
|
||||
if icon_path and os.path.exists(icon_path):
|
||||
try:
|
||||
self.window.setWindowIcon(QIcon(icon_path))
|
||||
except Exception as e:
|
||||
QMessageBox.warning(self.window, "警告", f"图标设置失败:{str(e)}")
|
||||
|
||||
central_widget = QWidget()
|
||||
self.window.setCentralWidget(central_widget)
|
||||
self.main_layout = QVBoxLayout(central_widget)
|
||||
self.main_layout.setContentsMargins(20, 20, 20, 20)
|
||||
self.main_layout.setSpacing(15)
|
||||
|
||||
def create_label(self, text, widget_id):
|
||||
if not self.window:
|
||||
self.create_window("默认窗口", 400, 300)
|
||||
label = QLabel(text)
|
||||
label.setMinimumHeight(30)
|
||||
self._get_current_layout().addWidget(label)
|
||||
self.widgets[widget_id] = label
|
||||
|
||||
def create_entry(self, hint, widget_id, readonly=False, input_type='text'):
|
||||
if not self.window:
|
||||
self.create_window("默认窗口", 400, 300)
|
||||
|
||||
container = QWidget()
|
||||
container.setMinimumHeight(30)
|
||||
layout = QHBoxLayout(container)
|
||||
layout.setContentsMargins(0, 0, 0, 0)
|
||||
layout.setSpacing(10)
|
||||
|
||||
label = QLabel(hint)
|
||||
entry = QLineEdit()
|
||||
entry.setReadOnly(readonly)
|
||||
if input_type == 'number':
|
||||
entry.setValidator(QIntValidator())
|
||||
|
||||
layout.addWidget(label)
|
||||
layout.addWidget(entry)
|
||||
self._get_current_layout().addWidget(container)
|
||||
self.widgets[widget_id] = entry
|
||||
self.variables[widget_id] = entry
|
||||
|
||||
def create_combobox(self, label_text, widget_id, options):
|
||||
if not self.window:
|
||||
self.create_window("默认窗口", 400, 300)
|
||||
|
||||
container = QWidget()
|
||||
container.setMinimumHeight(30)
|
||||
layout = QHBoxLayout(container)
|
||||
layout.setContentsMargins(0, 0, 0, 0)
|
||||
layout.setSpacing(10)
|
||||
|
||||
label = QLabel(label_text)
|
||||
combo = QComboBox()
|
||||
combo.addItems(options)
|
||||
|
||||
layout.addWidget(label)
|
||||
layout.addWidget(combo)
|
||||
self._get_current_layout().addWidget(container)
|
||||
self.widgets[widget_id] = combo
|
||||
self.variables[widget_id] = combo
|
||||
|
||||
def create_checkboxes(self, label_text, widget_id, options):
|
||||
if not self.window:
|
||||
self.create_window("默认窗口", 400, 300)
|
||||
|
||||
container = QWidget()
|
||||
container.setMinimumHeight(60)
|
||||
layout = QVBoxLayout(container)
|
||||
layout.setContentsMargins(0, 0, 0, 0)
|
||||
layout.setSpacing(5)
|
||||
|
||||
title_label = QLabel(label_text)
|
||||
layout.addWidget(title_label)
|
||||
|
||||
check_layout = QHBoxLayout()
|
||||
check_layout.setSpacing(15)
|
||||
checkboxes = []
|
||||
for opt in options:
|
||||
cb = QCheckBox(opt)
|
||||
check_layout.addWidget(cb)
|
||||
checkboxes.append(cb)
|
||||
|
||||
layout.addLayout(check_layout)
|
||||
self._get_current_layout().addWidget(container)
|
||||
self.widgets[widget_id] = checkboxes
|
||||
self.variables[widget_id] = checkboxes
|
||||
|
||||
def create_button(self, text, widget_id, action):
|
||||
if not self.window:
|
||||
self.create_window("默认窗口", 400, 300)
|
||||
|
||||
button = QPushButton(text)
|
||||
button.setMinimumHeight(30)
|
||||
button.setMaximumWidth(150)
|
||||
button.clicked.connect(lambda checked, a=action: self.handle_button_click(a))
|
||||
self._get_current_layout().addWidget(button, alignment=Qt.AlignLeft)
|
||||
self.widgets[widget_id] = button
|
||||
|
||||
def create_audio_player(self, audio_type, audio_path, audio_id):
|
||||
player = QMediaPlayer()
|
||||
self.media_players[audio_id] = {
|
||||
"player": player,
|
||||
"type": "audio"
|
||||
}
|
||||
|
||||
try:
|
||||
if audio_type == "url":
|
||||
media = QMediaContent(QUrl(audio_path))
|
||||
else:
|
||||
abs_path = os.path.abspath(audio_path).replace(" ", "%20") # 处理空格
|
||||
if not os.path.exists(abs_path.replace("%20", " ")):
|
||||
QMessageBox.warning(self.window, "警告", f"音频文件不存在:{abs_path.replace('%20', ' ')}")
|
||||
return
|
||||
media = QMediaContent(QUrl.fromLocalFile(abs_path))
|
||||
|
||||
player.setMedia(media)
|
||||
except Exception as e:
|
||||
QMessageBox.warning(self.window, "警告", f"音频加载失败:{str(e)}")
|
||||
|
||||
def create_image(self, img_type, img_path, img_id, width=None, height=None, tooltip=""):
|
||||
if not self.window:
|
||||
self.create_window("默认窗口", 400, 300)
|
||||
|
||||
container = QWidget()
|
||||
container.setMinimumHeight(height if height else 100)
|
||||
container.setMinimumWidth(width if width else 100)
|
||||
layout = QVBoxLayout(container)
|
||||
layout.setContentsMargins(0, 0, 0, 0)
|
||||
|
||||
img_label = QLabel()
|
||||
img_label.setToolTip(tooltip)
|
||||
img_label.setAlignment(Qt.AlignCenter)
|
||||
|
||||
pixmap = None
|
||||
try:
|
||||
if img_type == "path":
|
||||
if img_path.startswith(('http://', 'https://')):
|
||||
with urlopen(img_path) as response:
|
||||
img_data = response.read()
|
||||
image = QImage.fromData(img_data)
|
||||
pixmap = QPixmap.fromImage(image)
|
||||
else:
|
||||
abs_path = os.path.abspath(img_path)
|
||||
if os.path.exists(abs_path):
|
||||
pixmap = QPixmap(abs_path)
|
||||
else:
|
||||
img_label.setText("图片文件不存在")
|
||||
QMessageBox.warning(self.window, "警告", f"本地图片路径不存在:{abs_path}")
|
||||
|
||||
elif img_type == "url":
|
||||
with urlopen(img_path) as response:
|
||||
img_data = response.read()
|
||||
image = QImage.fromData(img_data)
|
||||
pixmap = QPixmap.fromImage(image)
|
||||
|
||||
elif img_type == "os":
|
||||
abs_path = os.path.abspath(img_path)
|
||||
if os.path.exists(abs_path):
|
||||
pixmap = QPixmap(abs_path)
|
||||
else:
|
||||
img_label.setText("图片文件不存在")
|
||||
QMessageBox.warning(self.window, "警告", f"本地图片路径不存在:{abs_path}")
|
||||
|
||||
except Exception as e:
|
||||
img_label.setText("图片加载失败")
|
||||
QMessageBox.warning(self.window, "警告", f"图片加载失败:{str(e)}")
|
||||
|
||||
if pixmap and not pixmap.isNull():
|
||||
if width and height:
|
||||
pixmap = pixmap.scaled(width, height, Qt.KeepAspectRatio, Qt.SmoothTransformation)
|
||||
elif width:
|
||||
pixmap = pixmap.scaledToWidth(width, Qt.SmoothTransformation)
|
||||
elif height:
|
||||
pixmap = pixmap.scaledToHeight(height, Qt.SmoothTransformation)
|
||||
|
||||
img_label.setPixmap(pixmap)
|
||||
|
||||
layout.addWidget(img_label)
|
||||
self._get_current_layout().addWidget(container)
|
||||
self.widgets[img_id] = img_label
|
||||
self.variables[img_id] = img_label
|
||||
|
||||
def create_slider(self, label_text, widget_id, min_val, max_val, value):
|
||||
if not self.window:
|
||||
self.create_window("默认窗口", 400, 300)
|
||||
|
||||
container = QWidget()
|
||||
container.setMinimumHeight(60)
|
||||
layout = QVBoxLayout(container)
|
||||
layout.setContentsMargins(0, 0, 0, 0)
|
||||
layout.setSpacing(5)
|
||||
|
||||
value_label = QLabel(f"{label_text}:{value}")
|
||||
slider = QSlider(Qt.Horizontal)
|
||||
slider.setRange(min_val, max_val)
|
||||
slider.setValue(value)
|
||||
slider.setTickInterval(1)
|
||||
slider.setTickPosition(QSlider.TicksBelow)
|
||||
slider.valueChanged.connect(lambda v: value_label.setText(f"{label_text}:{v}"))
|
||||
|
||||
layout.addWidget(value_label)
|
||||
layout.addWidget(slider)
|
||||
self._get_current_layout().addWidget(container)
|
||||
self.widgets[widget_id] = slider
|
||||
self.variables[widget_id] = slider
|
||||
|
||||
def create_textarea(self, label_text, widget_id, rows, readonly=False):
|
||||
if not self.window:
|
||||
self.create_window("默认窗口", 400, 300)
|
||||
|
||||
container = QWidget()
|
||||
layout = QVBoxLayout(container)
|
||||
layout.setContentsMargins(0, 0, 0, 0)
|
||||
layout.setSpacing(5)
|
||||
|
||||
label = QLabel(label_text)
|
||||
textarea = QTextEdit()
|
||||
textarea.setReadOnly(readonly)
|
||||
textarea.setMinimumHeight(rows * 25)
|
||||
|
||||
layout.addWidget(label)
|
||||
layout.addWidget(textarea)
|
||||
self._get_current_layout().addWidget(container)
|
||||
self.widgets[widget_id] = textarea
|
||||
self.variables[widget_id] = textarea
|
||||
|
||||
def create_separator(self, text, widget_id):
|
||||
if not self.window:
|
||||
self.create_window("默认窗口", 400, 300)
|
||||
|
||||
if text:
|
||||
container = QWidget()
|
||||
layout = QHBoxLayout(container)
|
||||
layout.setContentsMargins(0, 0, 0, 0)
|
||||
layout.setSpacing(10)
|
||||
|
||||
left_line = QFrame()
|
||||
left_line.setFrameShape(QFrame.HLine)
|
||||
left_line.setFrameShadow(QFrame.Sunken)
|
||||
|
||||
right_line = QFrame()
|
||||
right_line.setFrameShape(QFrame.HLine)
|
||||
right_line.setFrameShadow(QFrame.Sunken)
|
||||
|
||||
label = QLabel(text)
|
||||
layout.addWidget(left_line, 1)
|
||||
layout.addWidget(label, 0, Qt.AlignCenter)
|
||||
layout.addWidget(right_line, 1)
|
||||
|
||||
self._get_current_layout().addWidget(container)
|
||||
self.widgets[widget_id] = container
|
||||
else:
|
||||
line = QFrame()
|
||||
line.setFrameShape(QFrame.HLine)
|
||||
line.setFrameShadow(QFrame.Sunken)
|
||||
self._get_current_layout().addWidget(line)
|
||||
self.widgets[widget_id] = line
|
||||
|
||||
def create_progressbar(self, label_text, widget_id, min_val, max_val, value):
|
||||
if not self.window:
|
||||
self.create_window("默认窗口", 400, 300)
|
||||
|
||||
container = QWidget()
|
||||
container.setMinimumHeight(50)
|
||||
layout = QVBoxLayout(container)
|
||||
layout.setContentsMargins(0, 0, 0, 0)
|
||||
layout.setSpacing(5)
|
||||
|
||||
label = QLabel(label_text)
|
||||
progress = QProgressBar()
|
||||
progress.setRange(min_val, max_val)
|
||||
progress.setValue(value)
|
||||
progress.setTextVisible(True)
|
||||
|
||||
layout.addWidget(label)
|
||||
layout.addWidget(progress)
|
||||
self._get_current_layout().addWidget(container)
|
||||
self.widgets[widget_id] = progress
|
||||
self.variables[widget_id] = progress
|
||||
|
||||
def create_calendar(self, label_text, widget_id):
|
||||
if not self.window:
|
||||
self.create_window("默认窗口", 400, 300)
|
||||
|
||||
container = QWidget()
|
||||
layout = QVBoxLayout(container)
|
||||
layout.setContentsMargins(0, 0, 0, 0)
|
||||
layout.setSpacing(10)
|
||||
|
||||
label = QLabel(label_text)
|
||||
calendar = QCalendarWidget()
|
||||
calendar.setSelectionMode(QCalendarWidget.SingleSelection)
|
||||
|
||||
layout.addWidget(label)
|
||||
layout.addWidget(calendar)
|
||||
self._get_current_layout().addWidget(container)
|
||||
self.widgets[widget_id] = calendar
|
||||
self.variables[widget_id] = calendar
|
||||
|
||||
def create_radiogroup(self, label_text, widget_id, options):
|
||||
if not self.window:
|
||||
self.create_window("默认窗口", 400, 300)
|
||||
|
||||
container = QWidget()
|
||||
layout = QVBoxLayout(container)
|
||||
layout.setContentsMargins(0, 0, 0, 0)
|
||||
layout.setSpacing(5)
|
||||
|
||||
title_label = QLabel(label_text)
|
||||
layout.addWidget(title_label)
|
||||
|
||||
radio_buttons = []
|
||||
for i, opt in enumerate(options):
|
||||
radio = QRadioButton(opt)
|
||||
if i == 0:
|
||||
radio.setChecked(True)
|
||||
layout.addWidget(radio)
|
||||
radio_buttons.append(radio)
|
||||
|
||||
self._get_current_layout().addWidget(container)
|
||||
self.widgets[widget_id] = radio_buttons
|
||||
self.variables[widget_id] = radio_buttons
|
||||
|
||||
def create_groupbox(self, title, group_id):
|
||||
if not self.window:
|
||||
self.create_window("默认窗口", 400, 300)
|
||||
|
||||
groupbox = QGroupBox(title)
|
||||
group_layout = QVBoxLayout(groupbox)
|
||||
group_layout.setContentsMargins(15, 15, 15, 15)
|
||||
group_layout.setSpacing(10)
|
||||
|
||||
self._get_current_layout().addWidget(groupbox)
|
||||
self.groups[group_id] = group_layout
|
||||
self.widgets[group_id] = groupbox
|
||||
|
||||
def create_timer(self, timer_id, interval, action):
|
||||
if timer_id in self.timers:
|
||||
self.timers[timer_id]['timer'].stop()
|
||||
|
||||
timer = QTimer()
|
||||
timer.setInterval(interval)
|
||||
timer.timeout.connect(lambda: self.handle_timer_timeout(timer_id))
|
||||
self.timers[timer_id] = {
|
||||
'timer': timer,
|
||||
'action': action
|
||||
}
|
||||
|
||||
# ---------------------- 事件处理 ----------------------
|
||||
def _get_current_layout(self):
|
||||
return list(self.groups.values())[-1] if self.groups else self.main_layout
|
||||
|
||||
@pyqtSlot()
|
||||
def handle_timer_timeout(self, timer_id):
|
||||
if timer_id not in self.timers:
|
||||
return
|
||||
|
||||
timer_info = self.timers[timer_id]
|
||||
action = timer_info['action']
|
||||
|
||||
if action.startswith("update_progress="):
|
||||
try:
|
||||
progress_part, step_part = action.split(",")
|
||||
progress_id = progress_part.split("=")[1].strip()
|
||||
step = int(step_part.split("=")[1].strip())
|
||||
|
||||
progress_bar = self.widgets.get(progress_id)
|
||||
if not progress_bar or not isinstance(progress_bar, QProgressBar):
|
||||
return
|
||||
|
||||
current_value = progress_bar.value()
|
||||
new_value = current_value + step
|
||||
new_value = max(progress_bar.minimum(), min(progress_bar.maximum(), new_value))
|
||||
progress_bar.setValue(new_value)
|
||||
|
||||
if new_value >= progress_bar.maximum():
|
||||
timer_info['timer'].stop()
|
||||
|
||||
except Exception as e:
|
||||
QMessageBox.warning(self.window, "定时器错误", f"更新进度条失败:{str(e)}")
|
||||
|
||||
def handle_button_click(self, action):
|
||||
# 音频控制
|
||||
if action.startswith("play_audio="):
|
||||
self._control_audio(action.split("=")[1], "play")
|
||||
return
|
||||
if action.startswith("pause_audio="):
|
||||
self._control_audio(action.split("=")[1], "pause")
|
||||
return
|
||||
if action.startswith("stop_audio="):
|
||||
self._control_audio(action.split("=")[1], "stop")
|
||||
return
|
||||
|
||||
# 定时器控制
|
||||
if action.startswith("start_timer="):
|
||||
timer_id = action.split("=")[1].strip()
|
||||
self._control_timer(timer_id, "start")
|
||||
return
|
||||
if action.startswith("stop_timer="):
|
||||
timer_id = action.split("=")[1].strip()
|
||||
self._control_timer(timer_id, "stop")
|
||||
return
|
||||
|
||||
# 进度条控制
|
||||
if action.startswith("set_progress="):
|
||||
parts = action.split(",")
|
||||
if len(parts) >= 2 and parts[1].startswith("value="):
|
||||
try:
|
||||
p_id = parts[0].split("=")[1].strip()
|
||||
val = int(parts[1].split("=")[1].strip())
|
||||
if p_id in self.widgets and isinstance(self.widgets[p_id], QProgressBar):
|
||||
self.widgets[p_id].setValue(val)
|
||||
except Exception as e:
|
||||
QMessageBox.warning(self.window, "错误", f"设置进度条失败:{str(e)}")
|
||||
return
|
||||
|
||||
# 显示组件值
|
||||
if action.startswith("显示="):
|
||||
self._show_widget_value(action.split("=")[1].strip())
|
||||
return
|
||||
|
||||
def _control_audio(self, audio_id, action):
|
||||
if audio_id not in self.media_players or self.media_players[audio_id]["type"] != "audio":
|
||||
QMessageBox.warning(self.window, "警告", f"音频组件ID不存在:{audio_id}")
|
||||
return
|
||||
player = self.media_players[audio_id]["player"]
|
||||
if action == "play":
|
||||
player.play()
|
||||
elif action == "pause":
|
||||
player.pause()
|
||||
elif action == "stop":
|
||||
player.stop()
|
||||
|
||||
def _control_timer(self, timer_id, action):
|
||||
if timer_id not in self.timers:
|
||||
QMessageBox.warning(self.window, "警告", f"定时器ID不存在:{timer_id}")
|
||||
return
|
||||
timer = self.timers[timer_id]['timer']
|
||||
if action == "start":
|
||||
timer.start()
|
||||
elif action == "stop":
|
||||
timer.stop()
|
||||
|
||||
def _show_widget_value(self, widget_id):
|
||||
if widget_id not in self.variables:
|
||||
QMessageBox.warning(self.window, "警告", f"组件ID不存在:{widget_id}")
|
||||
return
|
||||
|
||||
target = self.variables[widget_id]
|
||||
msg = ""
|
||||
|
||||
if isinstance(target, list) and all(isinstance(x, QCheckBox) for x in target):
|
||||
selected = [cb.text() for cb in target if cb.isChecked()]
|
||||
msg = f"多选框选中项:{', '.join(selected) if selected else '无'}"
|
||||
elif isinstance(target, list) and all(isinstance(x, QRadioButton) for x in target):
|
||||
selected = [rb.text() for rb in target if rb.isChecked()]
|
||||
msg = f"单选框选中项:{', '.join(selected)}"
|
||||
elif isinstance(target, QComboBox):
|
||||
msg = f"下拉框选中:{target.currentText()}"
|
||||
elif isinstance(target, QLineEdit):
|
||||
msg = f"输入框内容:{target.text()}"
|
||||
elif isinstance(target, QSlider):
|
||||
msg = f"滑块值:{target.value()}"
|
||||
elif isinstance(target, QTextEdit):
|
||||
content = target.toPlainText()
|
||||
msg = f"文本区域内容:{content[:100]}..." if len(content) > 100 else f"文本区域内容:{content}"
|
||||
elif isinstance(target, QCalendarWidget):
|
||||
msg = f"选中日期:{target.selectedDate().toString('yyyy-MM-dd')}"
|
||||
elif isinstance(target, QProgressBar):
|
||||
msg = f"进度条值:{target.value()}%"
|
||||
elif isinstance(target, QLabel) and hasattr(target, 'pixmap') and target.pixmap():
|
||||
msg = f"图片信息:已加载图片({target.pixmap().width()}x{target.pixmap().height()})"
|
||||
|
||||
QMessageBox.information(self.window, "组件值", msg)
|
||||
|
||||
# ---------------------- 运行入口 ----------------------
|
||||
if __name__ == "__main__":
|
||||
if len(sys.argv) > 1:
|
||||
file_path = sys.argv[1]
|
||||
try:
|
||||
with open(file_path, 'r', encoding='utf-8') as f:
|
||||
ewui_code = f.read()
|
||||
interpreter = EasyUIInterpreter()
|
||||
interpreter.parse_and_run(ewui_code)
|
||||
except Exception as e:
|
||||
print(f"[EUI解释器错误]:{str(e)}", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
else:
|
||||
print("=" * 50)
|
||||
print("Easy UI 解释器(基础版)")
|
||||
print("用法:python easy_ui.py <EWUI文件路径>")
|
||||
print("支持组件:窗口、标签、按钮、输入框、下拉框、复选框等")
|
||||
print("=" * 50)
|
||||
sys.exit(0)
|
||||
Reference in New Issue
Block a user