#!/usr/bin/env python3 import os import sys import json import time import random import xpinyin import traceback import subprocess import PyQt5.QtGui as QtGui import PyQt5.QtCore as QtCore import PyQt5.QtWidgets as QtWidgets def ShowText(text: str): if text.replace(" ", "").replace("\n", "") == "": return logText.append(text.replace("\n", "")) def ErrorMessage(text: str): QtWidgets.QMessageBox.critical(window, "错误", text) def InformationMessage(text: str): QtWidgets.QMessageBox.information(window, "提示", text) questionChoose = False questionStatus = False def QuestionMessage(text: str): global questionChoose global questionStatus # 清零 questionChoose = False questionStatus = False if QtWidgets.QMessageBox.question(window, "提示", text) == QtWidgets.QMessageBox.Yes: questionChoose = True print(questionChoose) questionStatus = True return questionChoose = False questionStatus = True def DisbledAndEnabledAll(choose: bool): exePath.setDisabled(choose) browserExeButton.setDisabled(choose) buildButton.setDisabled(choose) # 获取用户主目录 def get_home(): return os.path.expanduser('~') def get_desktop_path(): try: for line in open(get_home() + "/.config/user-dirs.dirs"): # 以行来读取配置文件 desktop_index = line.find("XDG_DESKTOP_DIR=\"") # 寻找是否有对应项,有返回 0,没有返回 -1 if desktop_index != -1: # 如果有对应项 break # 结束循环 if desktop_index == -1: # 如果是提前结束,值一定≠-1,如果是没有提前结束,值一定=-1 return -1 else: get = line[17:-2] # 截取桌面目录路径 get_index = get.find("$HOME") # 寻找是否有对应的项,需要替换内容 if get != -1: # 如果有 get = get.replace("$HOME", get_home()) # 则把其替换为用户目录(~) return get # 返回目录 except: traceback.print_exc() return get_home() def CleanPressCompleteDownloadState(option): global pressCompleteDownload pressCompleteDownload = False installCmpleteButton.setEnabled(True) # 读取 lnk 文件 def GetLnkDesktop(path): lnkList = [] for i in os.listdir(path): filePath = f"{path}/{i}" if os.path.islink(filePath): # 忽略 link 链接 continue if os.path.isdir(filePath): lists = GetLnkDesktop(filePath) for k in lists: lnkList.append(k) continue if os.path.isfile(filePath) and os.path.splitext(filePath)[1] == ".lnk": with open(filePath, "rb") as file: while True: things = file.readline().lower() if things == b"": break print(things[1: -2].split("\x00".encode("gbk"))) for k in things[1: -2].split("\x00".encode("gbk")): if "c:".encode("gbk") in k: print(k.decode("gbk")) lnkList.append([filePath, k.decode("gbk")]) return lnkList def ReplaceText(string: str, lists: list): for i in lists: string = string.replace(i[0], i[1]) return string control = '''Package: @@@Package@@@ Version: @@@Version@@@ Architecture: i386 Maintainer: @@@Maintainer@@@ Depends: @@@Depends@@@ Section: non-free/otherosfs Priority: optional Multi-Arch: foreign Installed-Size: @@@Installed-Size@@@ Description: @@@Description@@@ ''' info = f'''{{ "appid": "@@@Package@@@", "name": "@@@Name@@@", "version": "@@@Version@@@", "arch": ["i386"], "permissions": {{ "autostart": false, "notification": false, "trayicon": true, "clipboard": true, "account": false, "bluetooth": false, "camera": true, "audio_record": true, "installed_apps": false }} }}''' postrm = f"""#!/bin/bash if [ "$1" = "remove" ] || [ "$1" = "purge" ];then echo "清理卸载残留" CONTAINER_NAME="@@@Package@@@" if [ -z $CONTAINER_NAME ];then echo "W: 没有指定容器,跳过清理容器。请手动前往 ~/.deepinwine/ 下删除" exit fi /opt/deepinwine/tools/kill.sh $CONTAINER_NAME ###这里注意,如果没写CONTAINER_NAME,会把QQ杀了 for username in $(ls /home) do echo /home/$username if [ -d /home/$username/.deepinwine/$CONTAINER_NAME ] then rm -rf /home/$username/.deepinwine/$CONTAINER_NAME fi done else echo "非卸载,跳过清理" fi""" runsh = f'''#!/bin/sh # Copyright (C) 2016 Deepin, Inc. # # Author: Li LongYu # Peng Hao # # # Copyright (C) 2022 The Spark Project # # # Modifier shenmo # # # #######################函数段。下文调用的额外功能会在此处声明 Get_Dist_Name() {{ if grep -Eqii "Deepin" /etc/issue || grep -Eq "Deepin" /etc/*-release; then DISTRO='Deepin' elif grep -Eqi "UnionTech" /etc/issue || grep -Eq "UnionTech" /etc/*-release; then DISTRO='UniontechOS' else DISTRO='OtherOS' fi }} ####获得发行版名称 #########################预设值段 version_gt() {{ test "$(echo "$@" | tr " " "\n" | sort -V | head -n 1)" != "$1"; }} ####用于比较版本?未实装 BOTTLENAME="@@@Package@@@" APPVER="@@@Version@@@" EXEC_PATH="@@@EXEC_PATH@@@" ##### 软件在wine中的启动路径 START_SHELL_PATH="/opt/deepinwine/tools/spark_run_v4.sh" export MIME_TYPE="" #####没什么用 export DEB_PACKAGE_NAME="@@@Package@@@" ####这里写包名才能在启动的时候正确找到files.7z,似乎也和杀残留进程有关 export APPRUN_CMD="deepin-wine6-stable" #####wine启动指令,建议 EXPORT_ENVS="" export SPECIFY_SHELL_DIR=`dirname $START_SHELL_PATH` ARCHIVE_FILE_DIR="/opt/apps/$DEB_PACKAGE_NAME/files" export WINEDLLPATH=/opt/$APPRUN_CMD/lib:/opt/$APPRUN_CMD/lib64 export WINEPREDLL="$ARCHIVE_FILE_DIR/dlls" DISABLE_ATTACH_FILE_DIALOG="" ##默认为空。若为1,则不使用系统自带的文件选择,而是使用wine的 ##对于deepin/UOS,大部分的应用都不需要使用wine的,如果有需求(比如wine应用选择的限定种类文件系统的文管不支持) ##请填1。 ##注意:因为非DDE的环境不确定,所以默认会在非Deepin/UOS发行版上禁用这个功能。如果你确认在适配的发行版上可以正常启动,请注释或者删除下面这段 ##############<<<<<<<<<禁用文件选择工具开始 Get_Dist_Name #此功能实现参见结尾函数段 if [ "$DISTRO" != "Deepin" ] && [ "$DISTRO" != "UniontechOS" ];then DISABLE_ATTACH_FILE_DIALOG="1" echo "非deepin/UOS,默认关闭系统自带的文件选择工具,使用Wine的" echo "如果你想改变这个行为,请到/opt/apps/$DEB_PACKAGE_NAME/files/$0处修改" echo "To打包者:如果你要打开自带请注意在适配的发行版上进行测试" echo "To用户:打包者没有打开这个功能,这证明启用这个功能可能造成运行问题。如果你要修改这个行为,请确保你有一定的动手能力" fi ##############>>>>>>>>>禁用文件选择工具结束 ##############<<<<<<<<<屏蔽mono和gecko安装器开始 ##默认屏蔽mono和gecko安装器 #if [ "$APPRUN_CMD" = "spark-wine7-devel" ];then #export WINEDLLOVERRIDES="mscoree,mshtml=" #echo "为了降低打包体积,默认关闭gecko和momo,如有需要,注释此行(仅对spark-wine7-devel有效)" #fi ##############>>>>>>>>>屏蔽mono和gecko安装器结束 #########################执行段 if [ -z "$DISABLE_ATTACH_FILE_DIALOG" ];then export ATTACH_FILE_DIALOG=1 fi if [ -n "$EXPORT_ENVS" ];then export $EXPORT_ENVS fi if [ -n "$EXEC_PATH" ];then if [ -z "${{EXEC_PATH##*.lnk*}}" ];then $START_SHELL_PATH $BOTTLENAME $APPVER "C:/windows/command/start.exe" "/Unix" "$EXEC_PATH" "$@" else $START_SHELL_PATH $BOTTLENAME $APPVER "$EXEC_PATH" "$@" fi else $START_SHELL_PATH $BOTTLENAME $APPVER "uninstaller.exe" "$@" fi''' desktopFile = f'''#!/usr/bin/env xdg-open [Desktop Entry] Encoding=UTF-8 Type=Application X-Created-By=@@@Maintainer@@@ Icon=@@@Icon@@@ Exec="/opt/apps/@@@Package@@@/files/run.sh" Name=@@@Name@@@ Comment=@@@Description@@@ MimeType= GenericName=@@@Package@@@ Terminal=false StartupNotify=false''' def getFileFolderSize(fileOrFolderPath): """get size for file or folder""" totalSize = 0 if not os.path.exists(fileOrFolderPath): return totalSize if os.path.isfile(fileOrFolderPath): totalSize = os.path.getsize(fileOrFolderPath) # 5041481 return totalSize if os.path.isdir(fileOrFolderPath): with os.scandir(fileOrFolderPath) as dirEntryList: for curSubEntry in dirEntryList: curSubEntryFullPath = os.path.join(fileOrFolderPath, curSubEntry.name) if curSubEntry.is_dir(): curSubFolderSize = getFileFolderSize(curSubEntryFullPath) # 5800007 totalSize += curSubFolderSize elif curSubEntry.is_file(): curSubFileSize = os.path.getsize(curSubEntryFullPath) # 1891 totalSize += curSubFileSize return totalSize def WriteTxt(path, things): with open(path, "w") as file: file.write(things) def ReadTxt(path): things = "" with open(path, "r") as file: things = file.read() return things def GetEXEVersion(exePath): versionPath = f"/tmp/wine-runner-exe-version-{random.randint(0, 1000)}.txt" if os.system(f"deepin-wine6-stable '{programPath}/GetEXEVersion.exe' '{exePath}' '{versionPath}'"): return "1.0.0" try: exeVersion = ReadTxt(versionPath).replace("\n", "") if exeVersion.replace(" ", "") == "": return "1.0.0" return exeVersion except: traceback.print_exc() return "1.0.0" def StrToByteToStr(text: str): lists = text.split("\\x") for i in range(len(lists)): lists[i] return text def UnUseUpperCharPath(path: str): pathList = [] lowerList = path.split("/")[1:] for i in lowerList: path = "/" + "/".join(pathList) before = len(pathList) for k in os.listdir(path): if k.lower() == i.lower(): pathList.append(k) break end = len(pathList) if before == end: raise OSError("文件路径不存在") return "/" + "/".join(pathList) def ReadMe(): QtWidgets.QMessageBox.information(window, "提示", """1、目前只支持打包 X86 架构的 deb 包,暂未支持 arm; 2、需要区分要打包的程序是绿色软件还是单文件安装包,两个对应的打包方式不相同; 3、打包绿色软件时为尽可能减小程序体积,请将绿化后的程序(或程序文件夹)单独拷贝到干净的目录后再浏览选择主程序打包; 4、打包详情: ①调用 Wine:Deepin Wine6 Stable ②调用 Helper:Spark Wine Helper ③有卸载自动移除容器脚本""") class RunThread(QtCore.QThread): showLogText = QtCore.pyqtSignal(str) error = QtCore.pyqtSignal(str) info = QtCore.pyqtSignal(str) question = QtCore.pyqtSignal(str) disbledAll = QtCore.pyqtSignal(bool) cleanPressState = QtCore.pyqtSignal(bool) def RunCommand(self, command): res = subprocess.Popen([command], shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) while res.poll() is None: try: text = res.stdout.readline().decode("utf8") except: text = "" self.showLogText.emit(text) print(text, end="") def __init__(self) -> None: super().__init__() def GetEXEVersion(self, exePath): versionPath = f"/tmp/wine-runner-exe-version-{random.randint(0, 1000)}.txt" self.RunCommand(f"deepin-wine6-stable '{programPath}/GetEXEVersion.exe' '{exePath}' '{versionPath}'") try: exeVersion = ReadTxt(versionPath).replace("\n", "") if exeVersion.replace(" ", "") == "": return "1.0.0" return exeVersion except: traceback.print_exc() return "1.0.0" def QuestionMsg(self, text): global questionStatus questionStatus = False self.question.emit(text) while not questionStatus: time.sleep(0.1) print(questionChoose) return questionChoose def run(self): try: self.disbledAll.emit(True) if not self.QuestionMsg("在此过程中,需要回答一系列的问题以进行打包,点击确定继续"): self.disbledAll.emit(False) return bottlePath = f"/tmp/deepin-wine-runner-bottle-{random.randint(0, 10000)}" # 清空容器以保证能正常使用 if os.path.exists(bottlePath): self.RunCommand(f"rm -rfv '{bottlePath}'") ############# 后面将全部调用 deepin wine6 stable 进行操作 exeName = os.path.basename(exePath.text()) # 暂定 packageName = xpinyin.Pinyin().get_pinyin(os.path.splitext(exeName)[0].replace(" ", ""), "").lower().replace(" ", "").replace("_", ".").replace("-", ".").replace("..", ".") if " " in packageName: packageName = "" for i in os.path.splitext(exeName)[0].split(" "): packageName += xpinyin.Pinyin().get_pinyin(i).lower().replace(" ", "").replace("_", ".").replace("-", ".").replace("..", ".") + "." print(packageName) packageName = packageName[:-1] debPackageName = "com." + packageName + ".spark" debPackageVersion = "1.0.0" programIconPath = f"/opt/apps/{debPackageName}/entries/icons/hicolor/scalable/apps/{debPackageName}.png" debMaintainer = os.getlogin() debBuildPath = f"/tmp/deepin-wine-packager-builder-{debPackageName}-{random.randint(0, 1000)}" bottlePackagePath = f"{debBuildPath}/opt/apps/{debPackageName}/files/files.7z" desktopPath = get_desktop_path() ############## 运行 EXE if self.QuestionMsg("请问此可执行文件是安装包还是绿色软件?是安装包请按 Yes,绿色软件按 No"): # 清空无益处的 lnk 文件 lnkPath = f"{bottlePath}/drive_c/ProgramData/Microsoft/Windows/Start Menu/Programs" self.RunCommand(f"rm -rfv '{lnkPath}'") self.RunCommand(f"mkdir -pv '{bottlePath}'") self.RunCommand(f"chmod 777 -Rv '{bottlePath}'") # 禁止生成 .desktop 文件 self.RunCommand(f"WINEPREFIX='{bottlePath}' deepin-wine6-stable 'reg' 'add' 'HKEY_CURRENT_USER\Software\Wine\DllOverrides' /v winemenubuilder.exe '/f'") # 安装包 self.info.emit("请在运行完安装程序后按下打包器主界面的“安装程序执行完成按钮”以进行下一步操作") global pressCompleteDownload pressCompleteDownload = False installCmpleteButton.setEnabled(True) self.RunCommand(f"WINEPREFIX='{bottlePath}' deepin-wine6-stable '{exePath.text()}' &") # 非堵塞线程 # 安装锁,锁解除后才可继续 while not pressCompleteDownload: time.sleep(0.1) # 杀死容器内应用 self.RunCommand(f"'{programPath}/kill.sh' '{os.path.basename(bottlePath)}'") # 识别 lnk lnkList = GetLnkDesktop(lnkPath) if len(lnkList) <= 0: self.error.emit("无法识别到任何 lnk 快捷方式") self.RunCommand(f"rm -rfv '{debBuildPath}' > /dev/null") self.RunCommand(f"rm -rfv '{bottlePath}' > /dev/null") self.disbledAll.emit(False) return # 选择最优 lnk secondChooseList = [] for k in lnkList: lnkPath = k[0].lower() lnkExePath = k[1].lower() if "卸载" in lnkPath or "uninstall" in lnkPath or "update" in lnkPath or "网页" in lnkPath or "websize" in lnkPath or not ".exe" in lnkExePath: continue secondChooseList.append(k) if len(secondChooseList) <= 0: secondChooseList = lnkList rightLnk = secondChooseList[0] miniLenge = len(rightLnk[1]) for k in secondChooseList: # 择优选择路径最短一项 if len(k[1]) < miniLenge: rightLnk = k miniLenge = len(rightLnk[1]) debPackageName = "com." + xpinyin.Pinyin().get_pinyin(os.path.splitext(os.path.basename(rightLnk[0]))[0].replace(" ", "")).lower().replace("--", "-").replace(" ", "").replace("_", "-").replace("-", ".") + ".spark" programIconPath = f"/opt/apps/{debPackageName}/entries/icons/hicolor/scalable/apps/{debPackageName}.png" bottlePackagePath = f"{debBuildPath}/opt/apps/{debPackageName}/files/files.7z" self.RunCommand(f"mkdir -pv '{debBuildPath}/opt/apps/{debPackageName}/entries/icons/hicolor/scalable/apps/'") folderExePath = os.path.dirname(rightLnk[1].replace("\\", "/").replace("c:/", bottlePath)) exePathInBottle = rightLnk[1] exeName = os.path.splitext(os.path.basename(folderExePath))[0] exePathInSystem = rightLnk[1].replace("\\", "/").replace("c:", f"{bottlePath}/drive_c") debPackageVersion = self.GetEXEVersion(exePathInSystem) cpNow = False for i in iconList: path = i[1].replace("wineBottonPath", bottlePath).lower() if path == exePathInSystem.lower(): self.RunCommand(f"cp -rv '{programPath}/Icon/{i[0]}.svg' '{debBuildPath}/{programIconPath}'") exeName = i[0] cpNow = True break if not cpNow: self.RunCommand(f"'{programPath}/wrestool' '{UnUseUpperCharPath(exePathInSystem)}' -x -t 14 > '{debBuildPath}/{programIconPath}'") else: #/home/gfdgd_xi/Desktop/新建文件夹1/BeCyIconGrabber.exe # 绿色软件 self.RunCommand(f"mkdir -pv '{bottlePath}'") self.RunCommand(f"chmod 777 -Rv '{bottlePath}'") self.RunCommand(f"WINEPREFIX='{bottlePath}' deepin-wine6-stable exit") folderExePath = os.path.dirname(exePath.text()) exePathInBottle = f"c:/Program Files/{os.path.basename(folderExePath)}/{exeName}" exeName = os.path.splitext(os.path.basename(os.path.basename(exePath.text())))[0] self.RunCommand(f"mkdir -pv '{debBuildPath}/opt/apps/{debPackageName}/entries/icons/hicolor/scalable/apps/'") self.RunCommand(f"'{programPath}/wrestool' '{exePath.text()}' -x -t 14 > '{debBuildPath}/{programIconPath}'") # 拷贝文件到容器 self.RunCommand(f"cp -rv '{folderExePath}' '{bottlePath}/drive_c/Program Files'") debPackageVersion = self.GetEXEVersion(exePath.text()) debDescription = f"{exeName} By Deepin Wine 6 Stable And Build By Wine Runner Easy Packager" debDepends = "deepin-wine6-stable, spark-dwine-helper | store.spark-app.spark-dwine-helper, fonts-wqy-microhei, fonts-wqy-zenhei" self.RunCommand(f"mkdir -pv '{debBuildPath}/DEBIAN'") self.RunCommand(f"mkdir -pv '{debBuildPath}/opt/apps/{debPackageName}/files'") self.RunCommand(f"mkdir -pv '{debBuildPath}/opt/apps/{debPackageName}/entries/applications'") #self.RunCommand(f"mkdir -pv '{debBuildPath}/opt/apps/{debPackageName}/entries/icons/hicolor/scalable/apps/'") ############ 处理容器 # 对用户目录进行处理 os.chdir(bottlePath) self.RunCommand("sed -i \"s#$USER#@current_user@#\" ./*.reg") os.chdir(f"{bottlePath}/drive_c/users") # 如果缩放文件 scale.txt 存在,需要移除以便用户自行调节缩放设置 if os.path.exists(f"{bottlePath}/scale.txt"): self.RunCommand(f"rm -rfv '{bottlePath}/scale.txt'") # 删除因为脚本失误导致用户目录嵌套(如果存在) if os.path.exists(f"{bottlePath}/drive_c/users/@current_user@/@current_user@"): self.RunCommand(f"rm -rfv '{bottlePath}/drive_c/users/@current_user@/@current_user@'") self.RunCommand(f"mv -fv '{os.getlogin()}' @current_user@") self.RunCommand(f"rm -fv '{bottlePath}/drive_c/users/@current_user@/我的'*") self.RunCommand(f"rm -fv '{bottlePath}/drive_c/users/@current_user@/My '*") self.RunCommand(f"rm -fv '{bottlePath}/drive_c/users/@current_user@/Desktop'") self.RunCommand(f"rm -fv '{bottlePath}/drive_c/users/@current_user@/Downloads'") self.RunCommand(f"rm -fv '{bottlePath}/drive_c/users/@current_user@/Templates'") ########### 打包容器 self.RunCommand(f"7z a '{bottlePackagePath}' '{bottlePath}/'*") ########### 生成文件内容 buildProgramSize = getFileFolderSize(debBuildPath) / 1000 replaceMap = [ ["@@@Package@@@", debPackageName], ["@@@Version@@@", debPackageVersion], ["@@@Maintainer@@@", debMaintainer], ["@@@Depends@@@", debDepends], ["@@@Description@@@", debDescription], ["@@@Installed-Size@@@", str(buildProgramSize)], ["@@@Name@@@", exeName], ["@@@EXEC_PATH@@@", exePathInBottle], ["@@@Icon@@@", programIconPath] ] debControl = ReplaceText(control, replaceMap) debPostrm = ReplaceText(postrm, replaceMap) debInfo = ReplaceText(info, replaceMap) debRunSh = ReplaceText(runsh, replaceMap) debDesktop = ReplaceText(desktopFile, replaceMap) ########### 写入文件 WriteTxt(f"{debBuildPath}/opt/apps/{debPackageName}/entries/applications/{debPackageName}.desktop", debDesktop) WriteTxt(f"{debBuildPath}/opt/apps/{debPackageName}/files/run.sh", debRunSh) WriteTxt(f"{debBuildPath}/opt/apps/{debPackageName}/info", debInfo) WriteTxt(f"{debBuildPath}/DEBIAN/control", debControl) WriteTxt(f"{debBuildPath}/DEBIAN/postrm", debPostrm) ########### 赋值权限 self.RunCommand(f"chmod -Rv 644 '{debBuildPath}/opt/apps/{debPackageName}/info'") self.RunCommand(f"chmod -Rv 0755 '{debBuildPath}/DEBIAN'") self.RunCommand(f"chmod -Rv 755 '{debBuildPath}/opt/apps/{debPackageName}/files/'*.sh") self.RunCommand(f"chmod -Rv 755 '{debBuildPath}/opt/apps/{debPackageName}/entries/applications/'*.desktop") ########### 打包 deb print(debPackageVersion) self.RunCommand(f"dpkg -b '{debBuildPath}' '{desktopPath}/{debPackageName}_{debPackageVersion}_i386.deb'") self.info.emit("打包完成!") self.disbledAll.emit(False) ########### 移除临时文件 self.RunCommand(f"rm -rfv '{debBuildPath}' > /dev/null") self.RunCommand(f"rm -rfv '{bottlePath}' > /dev/null") except: self.RunCommand(f"rm -rfv '{debBuildPath}' > /dev/null") self.RunCommand(f"rm -rfv '{bottlePath}' > /dev/null") # 若打包出现任何错误 traceback.print_exc() self.error.emit(f"打包错误,详细详细如下:{traceback.format_exc()}") self.showLogText.emit(traceback.format_exc()) self.disbledAll.emit(False) #/home/gfdgd_xi/Downloads/XPcalc.exe def RunBuildThread(): global buildThread buildThread = RunThread() buildThread.showLogText.connect(ShowText) buildThread.error.connect(ErrorMessage) buildThread.info.connect(InformationMessage) buildThread.question.connect(QuestionMessage) buildThread.disbledAll.connect(DisbledAndEnabledAll) buildThread.cleanPressState.connect(CleanPressCompleteDownloadState) logText.clear() buildThread.start() pressCompleteDownload = False def PressCompleteDownload(): global pressCompleteDownload pressCompleteDownload = True installCmpleteButton.setDisabled(True) def BrowserExe(): filePath = QtWidgets.QFileDialog.getOpenFileName(window, "选择 exe", get_home(), "可执行文件(*.exe);;所有文件(*.*)") if filePath[0] != "" or filePath[0] != None: exePath.setText(filePath[0]) if __name__ == "__main__": programPath = os.path.split(os.path.realpath(__file__))[0] # 返回 string iconPath = "{}/deepin-wine-runner.svg".format(programPath) information = json.loads(ReadTxt(f"{programPath}/information.json")) iconListUnBuild = json.loads(ReadTxt(f"{programPath}/IconList.json"))[0] iconList = json.loads(ReadTxt(f"{programPath}/IconList.json"))[1] for i in iconListUnBuild: iconList.append(i) app = QtWidgets.QApplication(sys.argv) version = information["Version"] window = QtWidgets.QMainWindow() widget = QtWidgets.QWidget() layout = QtWidgets.QGridLayout() exePath = QtWidgets.QLineEdit() browserExeButton = QtWidgets.QPushButton("浏览……") logText = QtWidgets.QTextBrowser() logText.setStyleSheet(""" background-color: black; color: white; """) controlLayout = QtWidgets.QHBoxLayout() buildButton = QtWidgets.QPushButton("现在打包……") installCmpleteButton = QtWidgets.QPushButton("安装程序执行完成") helpButton = QtWidgets.QPushButton("帮助") browserExeButton.clicked.connect(BrowserExe) buildButton.clicked.connect(RunBuildThread) installCmpleteButton.clicked.connect(PressCompleteDownload) helpButton.clicked.connect(ReadMe) installCmpleteButton.setDisabled(True) controlLayout.addWidget(buildButton) controlLayout.addWidget(installCmpleteButton) controlLayout.addWidget(helpButton) layout.addWidget(QtWidgets.QLabel("选择 EXE:"), 0, 0) layout.addWidget(exePath, 0, 1) layout.addWidget(browserExeButton, 0, 2) layout.addLayout(controlLayout, 1, 1) layout.addWidget(logText, 2, 0, 1, 3) widget.setLayout(layout) window.setCentralWidget(widget) window.setWindowTitle(f"Wine 运行器 {version}——简易打包器") try: exePath.setText(sys.argv[1]) except: pass window.resize(int(window.frameGeometry().width() * 1.2), int(window.frameGeometry().height() * 1.1)) window.show() sys.exit(app.exec_())