#!/usr/bin/env python3 # 本来是用C++写的,但在非deepin/UOS编译/运行就是下载不了https文件,只能用python重写 ######################################################################### # 作者:gfdgd xi # 版本:2.4.0 # 感谢:感谢 deepin-wine 团队,提供了 deepin-wine 给大家使用,让我能做这个程序 # 基于 Python3 的 PyQt5 构建 ######################################################################### ################# # 引入所需的库 ################# import os import shutil import random import sys import json import traceback import requests import webbrowser programPath = os.path.split(os.path.realpath(__file__))[0] # 返回 string sys.path.append(f"{programPath}/../") from Model import * from PyQt5 import QtCore, QtGui, QtWidgets programPath = os.path.split(os.path.realpath(__file__))[0] # 返回 string # UI 布局(自动生成) class Ui_MainWindow(object): def setupUi(self, MainWindow): MainWindow.setObjectName("MainWindow") MainWindow.resize(693, 404) self.centralWidget = QtWidgets.QWidget(MainWindow) self.centralWidget.setObjectName("centralWidget") self.verticalLayout_2 = QtWidgets.QVBoxLayout(self.centralWidget) self.verticalLayout_2.setContentsMargins(11, 11, 11, 11) self.verticalLayout_2.setSpacing(6) self.verticalLayout_2.setObjectName("verticalLayout_2") self.horizontalLayout_2 = QtWidgets.QHBoxLayout() self.horizontalLayout_2.setSpacing(6) self.horizontalLayout_2.setObjectName("horizontalLayout_2") self.localWineList = QtWidgets.QListView(self.centralWidget) self.localWineList.setObjectName("localWineList") self.horizontalLayout_2.addWidget(self.localWineList) self.verticalLayout = QtWidgets.QVBoxLayout() self.verticalLayout.setSpacing(6) self.verticalLayout.setObjectName("verticalLayout") spacerItem = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding) self.verticalLayout.addItem(spacerItem) self.addButton = QtWidgets.QPushButton(self.centralWidget) self.addButton.setObjectName("addButton") self.verticalLayout.addWidget(self.addButton) self.delButton = QtWidgets.QPushButton(self.centralWidget) self.delButton.setObjectName("delButton") self.verticalLayout.addWidget(self.delButton) spacerItem1 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding) self.verticalLayout.addItem(spacerItem1) self.horizontalLayout_2.addLayout(self.verticalLayout) self.internetWineList = QtWidgets.QListView(self.centralWidget) self.internetWineList.setObjectName("internetWineList") self.horizontalLayout_2.addWidget(self.internetWineList) self.verticalLayout_2.addLayout(self.horizontalLayout_2) self.horizontalLayout = QtWidgets.QHBoxLayout() self.horizontalLayout.setSpacing(6) self.horizontalLayout.setObjectName("horizontalLayout") self.unzip = QtWidgets.QCheckBox(self.centralWidget) self.unzip.setObjectName("unzip") self.horizontalLayout.addWidget(self.unzip) self.deleteZip = QtWidgets.QCheckBox(self.centralWidget) self.deleteZip.setChecked(True) self.deleteZip.setTristate(False) self.deleteZip.setObjectName("deleteZip") self.horizontalLayout.addWidget(self.deleteZip) self.addOtherWine = QtWidgets.QPushButton(self.centralWidget) self.downloadWineFromCloudDisk = QtWidgets.QPushButton(self.centralWidget) self.horizontalLayout.addWidget(self.downloadWineFromCloudDisk) self.horizontalLayout.addWidget(self.addOtherWine) spacerItem2 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) self.horizontalLayout.addItem(spacerItem2) self.verticalLayout_2.addLayout(self.horizontalLayout) MainWindow.setCentralWidget(self.centralWidget) # 菜单栏 _translate = QtCore.QCoreApplication.translate self.menu = MainWindow.menuBar() self.changeSources = self.menu.addMenu(_translate("MainWindow", "更换源")) self.gitlinkAction = QtWidgets.QAction(_translate("MainWindow", "Gitee 源(推荐)")) self.kgithubAction = QtWidgets.QAction(_translate("MainWindow", "Sourceforge 源")) self.ipv6Action = QtWidgets.QAction(_translate("MainWindow", "默认源")) self.githubAction = QtWidgets.QAction(_translate("MainWindow", "Github 源(国内访问不稳定)")) self.localAction = QtWidgets.QAction(_translate("MainWindow", "本地测试源(127.0.0.1)")) self.changeSources.addAction(self.ipv6Action) self.changeSources.addAction(self.kgithubAction) self.changeSources.addAction(self.gitlinkAction) self.changeSources.addAction(self.githubAction) self.changeSources.addAction(self.localAction) for i in [self.gitlinkAction, self.ipv6Action, self.localAction, self.kgithubAction, self.githubAction]: i.setCheckable(True) self.ipv6Action.setChecked(True) self.changeSourcesGroup = QtWidgets.QActionGroup(MainWindow) self.changeSourcesGroup.addAction(self.gitlinkAction) self.changeSourcesGroup.addAction(self.kgithubAction) self.changeSourcesGroup.addAction(self.ipv6Action) self.changeSourcesGroup.addAction(self.githubAction) self.changeSourcesGroup.addAction(self.localAction) self.retranslateUi(MainWindow) QtCore.QMetaObject.connectSlotsByName(MainWindow) def retranslateUi(self, MainWindow): _translate = QtCore.QCoreApplication.translate MainWindow.setWindowTitle(_translate("MainWindow", "下载 Wine")) self.addButton.setText(_translate("MainWindow", "<<")) self.delButton.setText(_translate("MainWindow", ">>")) self.unzip.setText(_translate("MainWindow", "不解压Wine资源文件")) self.deleteZip.setText(_translate("MainWindow", "删除下载的资源包,只解压保留(两个选项都选相互抵消)")) self.addOtherWine.setText(_translate("MainWindow", "导入 Wine 7z 包")) self.downloadWineFromCloudDisk.setText(_translate("MainWindow", "从网盘下载 Wine")) def ReadLocalInformation(): try: global localJsonList file = open(f"{programPath}/winelist.json", "r") localJsonList = json.loads(file.read()) nmodel = QtGui.QStandardItemModel(window) for i in localJsonList: item = QtGui.QStandardItem(i) nmodel.appendRow(item) ui.localWineList.setModel(nmodel) file.close() except: print("新建空列表") try: with open(f"{programPath}/winelist.json", "w") as file: file.write("[]") except: traceback.print_exc() QtWidgets.QMessageBox.critical(window, "错误", traceback.format_exc()) def InstallOtherWine(): path = QtWidgets.QFileDialog.getOpenFileName(window, "选择 Wine 运行器 Wine 7z 包", os.getenv("~"), "Wine 运行器 Wine 7z 包(*.7z);;全部文件(*.*)") if path[0] == "" or not path[1]: return try: # 写入配置文件 rfile = open(f"{programPath}/winelist.json", "r") list = json.loads(rfile.read()) rfile.close() name = os.path.splitext(os.path.basename(path[0]))[0] removeSymbol = ["(", ")", "(", ")", " ", "!", "@", "#", "$", "%", "^", "&", "*", "=", "[", "]", "{", "}", "\\", "/", "|", "?", "<", ">", "·", "「", ";", ":", ":", "?", "《", "》", ",", "。", ",", ".", "`", "~", "~", "、"] for i in removeSymbol: name.replace(i, "") unpackPath = f"{programPath}/{name}" shellCommand = f"""mkdir -p \"{unpackPath}\" 7z x -y \"{path[0]}\" -o\"{unpackPath}\" """ shellFile = open("/tmp/depein-wine-runner-wine-install.sh", "w") shellFile.write(shellCommand) shellFile.close() OpenTerminal("bash /tmp/depein-wine-runner-wine-install.sh") # C++ 版注释:不直接用 readwrite 是因为不能覆盖写入 file = open(f"{programPath}/winelist.json", "w") list.append(name) file.write(json.dumps(list)) file.close() except: traceback.print_exc() QtWidgets.QMessageBox.critical(window, "错误", traceback.format_exc()) ReadLocalInformation() def ChangeSources(): global urlSources global internetWineSource sources = [ui.ipv6Action, ui.kgithubAction, ui.gitlinkAction, ui.githubAction, ui.localAction] for i in range(0, len(sources)): if sources[i].isChecked(): urlSources = internetWineSourceList[i] internetWineSource = internetWineSourceList[i] # 读取信息 ReadLocalInformation() ReadInternetInformation() break print(urlSources) # 下面内容均翻译自 C++ 版本 def ReadInternetInformation(): global internetJsonList # C++ 版本是用 curl 的,考虑到 Python 用 requests 反而方便,于是不用 curl try: text = requests.get(f"{internetWineSource}/information.json").text print(text) internetJsonList = json.loads(text) except: traceback.print_exc() QtWidgets.QMessageBox.critical(window, "错误", "无法连接服务器!") return nmodel = QtGui.QStandardItemModel(window) for i in internetJsonList: item = QtGui.QStandardItem(i[0]) nmodel.appendRow(item) ui.internetWineList.setModel(nmodel) class DownloadThread(QtCore.QThread): MessageBoxInfo = QtCore.pyqtSignal(str) MessageBoxError = QtCore.pyqtSignal(str) ChangeDialog = QtCore.pyqtSignal(QtWidgets.QProgressDialog, int, int, int) Finish = QtCore.pyqtSignal() def __init__(self, progressDialog: QtWidgets.QProgressDialog, url: str, savePath: str, fileName: str, view: QtWidgets.QListView, deleteZip: bool, unzip: bool, localList) -> None: self.dialog = progressDialog self.fileUrl = url self.fileSavePath = savePath self.fileSaveName = fileName self.localView = view self.downloadDeleteZip = deleteZip self.downloadUnzip = unzip self.localJsonList = localList super().__init__() def ReadLocalInformation(self): global localJsonList try: file = open(f"{programPath}/winelist.json", "r"); nmodel = QtGui.QStandardItemModel() localJsonList = json.loads(file.read()) for i in localJsonList: nmodel.appendRow(QtGui.QStandardItem(i)) self.localView.setModel(nmodel) file.close() except: traceback.print_exc() QtWidgets.QMessageBox.critical(window, "错误", traceback.format_exc()) def run(self): try: # 创建文件夹 dir = QtCore.QDir() savePath = f"{programPath}/{self.fileSaveName}" # 文件下载 timeout = 0 f = requests.get(self.fileUrl, stream=True) allSize = int(f.headers["content-length"]) # 文件总大小 bytesRead = 0 with open(savePath, "wb") as filePart: for chunk in f.iter_content(chunk_size=1024): if chunk: #progressbar.update(int(part / show)) filePart.write(chunk) bytesRead += 1024 self.ChangeDialog.emit(self.dialog, int(bytesRead / allSize * 100), int(bytesRead / 1024 / 1024), int(allSize / 1024 / 1024)) # 写入配置文件 rfile = open(f"{programPath}/winelist.json", "r") list = json.loads(rfile.read()) rfile.close() # C++ 版注释:不直接用 readwrite 是因为不能覆盖写入 file = open(f"{programPath}/winelist.json", "w") list.append(self.fileSaveName.replace(".7z", "")) file.write(json.dumps(list)) file.close() # 读取配置文件 self.ReadLocalInformation() self.localJsonList = list # 解压文件 shellCommand = "" if self.downloadUnzip: path = f"{programPath}/{self.fileSaveName.replace('.7z', '')}" shellCommand += f"""mkdir -p \"{path}\" 7z x -y \"{savePath}\" -o\"{path}\" """ if self.downloadDeleteZip: shellCommand += f"rm -rf \"{savePath}\"\n" shellFile = open("/tmp/depein-wine-runner-wine-install.sh", "w") shellFile.write(shellCommand) shellFile.close() #process = QtCore.QProcess() #command = ["deepin-terminal", "-e", "bash", "/tmp/depein-wine-runner-wine-install.sh"] #process.start(f"{programPath}/../launch.sh", command) #process.waitForFinished() OpenTerminal("bash /tmp/depein-wine-runner-wine-install.sh") self.Finish.emit() except: traceback.print_exc() self.MessageBoxError.emit(traceback.format_exc()) self.ChangeDialog.emit(self.dialog, 100, 100, 100) self.Finish.emit() def MessageBoxInfo(info): QtWidgets.QMessageBox.information(window, "提示", info) def MessageBoxError(info): QtWidgets.QMessageBox.critical(window, "错误", info) def ChangeDialog(dialog: QtWidgets.QProgressDialog, value, downloadBytes, totalBytes): dialog.setValue(value) dialog.setLabelText(f"{downloadBytes}MB/{totalBytes}MB") def DownloadFinish(): ui.centralWidget.setEnabled(True) class QT: thread = None def on_addButton_clicked(): choose = ui.internetWineList.currentIndex().row() if choose < 0: QtWidgets.QMessageBox.information(window, "提示", "您未选中任何项,无法继续") return downloadName = internetJsonList[choose][1] ReadLocalInformation() for i in localJsonList: if i == internetJsonList[choose][0]: QtWidgets.QMessageBox.information(window, "提示", "您已经安装了这个Wine了!无需重复安装!") return if(ui.deleteZip.isChecked() + ui.unzip.isChecked() == 2): ui.deleteZip.setChecked(False) ui.unzip.setChecked(False) #downloadUrl = internetWineSource + downloadName if "://" in downloadName: downloadUrl = downloadName else: downloadUrl = internetWineSource + downloadName dialog = QtWidgets.QProgressDialog() cancel = QtWidgets.QPushButton("取消") cancel.setDisabled(True) dialog.setWindowIcon(QtGui.QIcon(f"{programPath}/../deepin-wine-runner.svg")) dialog.setCancelButton(cancel) dialog.setWindowTitle(f"正在下载“{internetJsonList[choose][0]}”") saveName = os.path.basename(internetJsonList[choose][1]) if os.path.split(os.path.basename(internetJsonList[choose][1]))[0] == "": saveName = os.path.basename(internetJsonList[choose][0]) + ".7z" QT.thread = DownloadThread( dialog, downloadUrl, "", saveName, ui.localWineList, ui.deleteZip.isChecked(), not ui.unzip.isChecked(), localJsonList ) QT.thread.MessageBoxInfo.connect(MessageBoxInfo) QT.thread.MessageBoxError.connect(MessageBoxError) QT.thread.ChangeDialog.connect(ChangeDialog) QT.thread.Finish.connect(DownloadFinish) ui.centralWidget.setDisabled(True) QT.thread.start() def on_delButton_clicked(): if QtWidgets.QMessageBox.question(window, "提示", "你确定要删除吗?") == QtWidgets.QMessageBox.No: return if ui.localWineList.currentIndex().row() < 0: QtWidgets.QMessageBox.information(window, "提示", "您未选择任何项") return try: name = f"{programPath}/{localJsonList[ui.localWineList.currentIndex().row()]}" dir = QtCore.QDir(name) dir.removeRecursively() QtCore.QFile.remove(name + ".7z") del localJsonList[ui.localWineList.currentIndex().row()] file = open(f"{programPath}/winelist.json", "w") file.write(json.dumps(localJsonList)) file.close() ReadLocalInformation() QtWidgets.QMessageBox.information(window, "提示", "删除成功!") except: traceback.print_exc() QtWidgets.QMessageBox.critical(window, "错误", traceback.format_exc()) def on_downloadWineFromCloudDisk_clicked(): QtWidgets.QMessageBox.information(window, "提示", "即将在浏览器打开下载页面\n访问密码为 2061") webbrowser.open_new_tab("http://ctfile.gfdgdxi.top/d/31540479-61624693-080e74?p=2061") # 获取当前语言 def get_now_lang()->"获取当前语言": return os.getenv('LANG') if __name__ == "__main__": localJsonList = [] internetJsonList = [] internetWineSourceList = [ "http://wine.wine-runner.gfdgdxi.top/", "http://wine.wine-runner.gfdgdxi.top/sourceforge", "https://gitee.com/gfdgd-xi/wine-mirrors-websize/raw/master/", "https://github.com/gfdgd-xi/wine-mirrors-websize/raw/master/", "http://127.0.0.1/wine-mirrors/" # 本地测试源 ] app = QtWidgets.QApplication(sys.argv) # 读取翻译 if not get_now_lang() == "zh_CN.UTF-8": trans = QtCore.QTranslator() trans.load(f"{programPath}/../LANG/installwine-en_US.qm") app.installTranslator(trans) # 窗口构建 window = QtWidgets.QMainWindow() ui = Ui_MainWindow() window.setWindowIcon(QtGui.QIcon(f"{programPath}/../deepin-wine-runner.svg")) ui.setupUi(window) window.show() # 判断机器所在国家并自动分配源 try: isChina = requests.get("https://ip.useragentinfo.com/json").json()["country"] == "中国" print("IsChina", isChina) if isChina: internetWineSource = internetWineSourceList[0] ui.ipv6Action.setChecked(True) else: internetWineSource = internetWineSourceList[1] ui.kgithubAction.setChecked(True) except: traceback.print_exc() print("IsChina", False) # 请求失败,默认使用国际源 internetWineSource = internetWineSourceList[1] ui.kgithubAction.setChecked(True) # 连接信号 ui.addButton.clicked.connect(on_addButton_clicked) ui.delButton.clicked.connect(on_delButton_clicked) ui.downloadWineFromCloudDisk.clicked.connect(on_downloadWineFromCloudDisk_clicked) ui.addOtherWine.clicked.connect(InstallOtherWine) ui.changeSourcesGroup.triggered.connect(ChangeSources) ## 加载内容 # 设置列表双击不会编辑 ui.localWineList.setEditTriggers(QtWidgets.QAbstractItemView.NoEditTriggers) ui.internetWineList.setEditTriggers(QtWidgets.QAbstractItemView.NoEditTriggers) # 读取信息 ReadLocalInformation() ReadInternetInformation() # 图标 ui.centralWidget.setWindowIcon(QtGui.QIcon(f"{programPath}/../deepin-wine-runner.svg")) app.exec_()