deep-wine-runner/wine/installwine
gfdgd_xi f1decf51d1
Some checks are pending
Auto Building Wine Runner(rpm) / Explore-GitHub-Actions (push) Waiting to run
Auto Building Wine Runner(deb) / Explore-GitHub-Actions (push) Waiting to run
支持导入.7z包时自动移除部分特殊符号
2024-07-15 21:46:24 +08:00

436 lines
19 KiB
Python
Executable File
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/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_()