deep-wine-runner/wine/installwine

436 lines
19 KiB
Plaintext
Raw Normal View History

2022-08-22 17:30:42 +08:00
#!/usr/bin/env python3
# 本来是用C++写的但在非deepin/UOS编译/运行就是下载不了https文件只能用python重写
#########################################################################
2023-12-23 12:24:20 +08:00
# 作者gfdgd xi
# 版本2.4.0
2022-08-22 17:30:42 +08:00
# 感谢:感谢 deepin-wine 团队,提供了 deepin-wine 给大家使用,让我能做这个程序
# 基于 Python3 的 PyQt5 构建
#########################################################################
#################
# 引入所需的库
#################
import os
import shutil
import random
2022-08-22 17:30:42 +08:00
import sys
import json
import traceback
2022-09-24 21:02:50 +08:00
import requests
2024-07-15 15:14:20 +08:00
import webbrowser
2022-10-01 20:53:50 +08:00
programPath = os.path.split(os.path.realpath(__file__))[0] # 返回 string
sys.path.append(f"{programPath}/../")
from Model import *
2022-08-22 17:30:42 +08:00
from PyQt5 import QtCore, QtGui, QtWidgets
2022-10-01 20:53:50 +08:00
programPath = os.path.split(os.path.realpath(__file__))[0] # 返回 string
2022-08-22 17:30:42 +08:00
# 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)
2024-07-15 15:14:20 +08:00
self.downloadWineFromCloudDisk = QtWidgets.QPushButton(self.centralWidget)
self.horizontalLayout.addWidget(self.downloadWineFromCloudDisk)
self.horizontalLayout.addWidget(self.addOtherWine)
2022-08-22 17:30:42 +08:00
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)
2022-11-06 17:35:41 +08:00
# 菜单栏
_translate = QtCore.QCoreApplication.translate
self.menu = MainWindow.menuBar()
self.changeSources = self.menu.addMenu(_translate("MainWindow", "更换源"))
2023-06-29 17:03:46 +08:00
self.gitlinkAction = QtWidgets.QAction(_translate("MainWindow", "Gitee 源(推荐)"))
2023-11-25 20:26:20 +08:00
self.kgithubAction = QtWidgets.QAction(_translate("MainWindow", "Sourceforge 源"))
self.ipv6Action = QtWidgets.QAction(_translate("MainWindow", "默认源"))
self.githubAction = QtWidgets.QAction(_translate("MainWindow", "Github 源(国内访问不稳定)"))
2023-04-05 16:09:58 +08:00
2022-11-06 17:35:41 +08:00
self.localAction = QtWidgets.QAction(_translate("MainWindow", "本地测试源127.0.0.1"))
self.changeSources.addAction(self.ipv6Action)
2023-05-08 20:56:48 +08:00
self.changeSources.addAction(self.kgithubAction)
self.changeSources.addAction(self.gitlinkAction)
2023-04-05 16:09:58 +08:00
self.changeSources.addAction(self.githubAction)
2022-11-06 17:35:41 +08:00
self.changeSources.addAction(self.localAction)
2023-04-05 16:09:58 +08:00
for i in [self.gitlinkAction, self.ipv6Action, self.localAction, self.kgithubAction, self.githubAction]:
2022-11-06 17:35:41 +08:00
i.setCheckable(True)
2023-05-08 20:56:48 +08:00
self.ipv6Action.setChecked(True)
2022-11-06 17:35:41 +08:00
self.changeSourcesGroup = QtWidgets.QActionGroup(MainWindow)
self.changeSourcesGroup.addAction(self.gitlinkAction)
2023-04-05 16:09:58 +08:00
self.changeSourcesGroup.addAction(self.kgithubAction)
2022-11-06 17:35:41 +08:00
self.changeSourcesGroup.addAction(self.ipv6Action)
2023-04-05 16:09:58 +08:00
self.changeSourcesGroup.addAction(self.githubAction)
2022-11-06 17:35:41 +08:00
self.changeSourcesGroup.addAction(self.localAction)
2022-08-22 17:30:42 +08:00
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", "删除下载的资源包,只解压保留(两个选项都选相互抵消)"))
2024-07-15 20:46:21 +08:00
self.addOtherWine.setText(_translate("MainWindow", "导入 Wine 7z 包"))
2024-07-15 15:14:20 +08:00
self.downloadWineFromCloudDisk.setText(_translate("MainWindow", "从网盘下载 Wine"))
2022-08-22 17:30:42 +08:00
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:
2022-11-15 14:09:48 +08:00
print("新建空列表")
try:
with open(f"{programPath}/winelist.json", "w") as file:
file.write("[]")
except:
traceback.print_exc()
QtWidgets.QMessageBox.critical(window, "错误", traceback.format_exc())
2022-08-22 17:30:42 +08:00
def InstallOtherWine():
2024-07-15 20:46:21 +08:00
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()
2024-07-15 20:46:21 +08:00
name = os.path.splitext(os.path.basename(path[0]))[0]
removeSymbol = ["(", ")", "", "", " ", "!", "@", "#",
"$", "%", "^", "&", "*", "=", "[", "]",
"{", "}", "\\", "/", "|", "?",
"<", ">", "·", "「", ";", ":", "",
"", "《", "》", "", "。", ",", ".",
"`", "~", "", "、"]
for i in removeSymbol:
name.replace(i, "")
2024-07-15 20:46:21 +08:00
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()
2022-11-06 17:35:41 +08:00
def ChangeSources():
global urlSources
global internetWineSource
2023-05-08 20:56:48 +08:00
sources = [ui.ipv6Action, ui.kgithubAction, ui.gitlinkAction, ui.githubAction, ui.localAction]
2022-11-06 17:35:41 +08:00
for i in range(0, len(sources)):
if sources[i].isChecked():
urlSources = internetWineSourceList[i]
internetWineSource = internetWineSourceList[i]
2022-11-06 17:35:41 +08:00
# 读取信息
ReadLocalInformation()
ReadInternetInformation()
break
print(urlSources)
2022-11-06 17:35:41 +08:00
2022-08-22 17:30:42 +08:00
# 下面内容均翻译自 C++ 版本
def ReadInternetInformation():
global internetJsonList
# C++ 版本是用 curl 的,考虑到 Python 用 requests 反而方便,于是不用 curl
2022-08-27 02:00:41 +08:00
try:
2023-06-29 17:03:46 +08:00
text = requests.get(f"{internetWineSource}/information.json").text
print(text)
internetJsonList = json.loads(text)
2022-08-27 02:00:41 +08:00
except:
2023-04-05 16:09:58 +08:00
traceback.print_exc()
2022-08-27 02:00:41 +08:00
QtWidgets.QMessageBox.critical(window, "错误", "无法连接服务器!")
return
2022-08-22 17:30:42 +08:00
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))
2022-08-22 17:30:42 +08:00
# 写入配置文件
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}\"
2022-08-22 17:30:42 +08:00
"""
if self.downloadDeleteZip:
shellCommand += f"rm -rf \"{savePath}\"\n"
shellFile = open("/tmp/depein-wine-runner-wine-install.sh", "w")
shellFile.write(shellCommand)
shellFile.close()
2022-10-01 20:53:50 +08:00
#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")
2022-08-22 17:30:42 +08:00
self.Finish.emit()
except:
traceback.print_exc()
self.MessageBoxError.emit(traceback.format_exc())
self.ChangeDialog.emit(self.dialog, 100, 100, 100)
self.Finish.emit()
2022-08-22 17:30:42 +08:00
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)
2023-04-05 16:09:58 +08:00
#downloadUrl = internetWineSource + downloadName
if "://" in downloadName:
downloadUrl = downloadName
else:
downloadUrl = internetWineSource + downloadName
2022-08-22 17:30:42 +08:00
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]}”")
2023-11-25 20:26:20 +08:00
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"
2022-08-22 17:30:42 +08:00
QT.thread = DownloadThread(
dialog,
downloadUrl,
"",
2023-11-25 20:26:20 +08:00
saveName,
2022-08-22 17:30:42 +08:00
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())
2024-07-15 15:14:20 +08:00
def on_downloadWineFromCloudDisk_clicked():
QtWidgets.QMessageBox.information(window, "提示", "即将在浏览器打开下载页面\n访问密码为 2061")
webbrowser.open_new_tab("http://ctfile.gfdgdxi.top/d/31540479-61624693-080e74?p=2061")
2022-08-24 16:13:47 +08:00
# 获取当前语言
def get_now_lang()->"获取当前语言":
return os.getenv('LANG')
2022-08-22 17:30:42 +08:00
if __name__ == "__main__":
localJsonList = []
internetJsonList = []
2022-11-06 17:35:41 +08:00
internetWineSourceList = [
2023-04-05 16:09:58 +08:00
"http://wine.wine-runner.gfdgdxi.top/",
"http://wine.wine-runner.gfdgdxi.top/sourceforge",
2023-06-29 17:03:46 +08:00
"https://gitee.com/gfdgd-xi/wine-mirrors-websize/raw/master/",
2023-04-05 16:09:58 +08:00
"https://github.com/gfdgd-xi/wine-mirrors-websize/raw/master/",
2022-11-05 08:50:19 +08:00
"http://127.0.0.1/wine-mirrors/" # 本地测试源
2022-11-06 17:35:41 +08:00
]
2022-08-22 17:30:42 +08:00
app = QtWidgets.QApplication(sys.argv)
# 读取翻译
2022-08-24 16:13:47 +08:00
if not get_now_lang() == "zh_CN.UTF-8":
trans = QtCore.QTranslator()
trans.load(f"{programPath}/../LANG/installwine-en_US.qm")
app.installTranslator(trans)
2022-08-22 17:30:42 +08:00
# 窗口构建
window = QtWidgets.QMainWindow()
ui = Ui_MainWindow()
window.setWindowIcon(QtGui.QIcon(f"{programPath}/../deepin-wine-runner.svg"))
2022-08-22 17:30:42 +08:00
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)
2022-08-22 17:30:42 +08:00
# 连接信号
ui.addButton.clicked.connect(on_addButton_clicked)
ui.delButton.clicked.connect(on_delButton_clicked)
2024-07-15 15:14:20 +08:00
ui.downloadWineFromCloudDisk.clicked.connect(on_downloadWineFromCloudDisk_clicked)
ui.addOtherWine.clicked.connect(InstallOtherWine)
2022-11-06 17:35:41 +08:00
ui.changeSourcesGroup.triggered.connect(ChangeSources)
2022-08-22 17:30:42 +08:00
## 加载内容
# 设置列表双击不会编辑
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_()