-
Notifications
You must be signed in to change notification settings - Fork 15
/
Copy pathQemuDownload.py
executable file
·442 lines (422 loc) · 20.1 KB
/
QemuDownload.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
#!/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 updatekiller
import json
import traceback
import requests
programPath = os.path.split(os.path.realpath(__file__))[0] # 返回 string
sys.path.append(f"{programPath}/../")
from Model import *
from trans 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)
if not get_now_lang() == "zh_CN.UTF-8":
transla = Trans("en_US", f"{programPath}/trans/deepin-wine-runner-qemu-download.json")
else:
transla = Trans("zh_CN")
_translate = transla.transe
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")
self.verticalLayout_2.addWidget(QtWidgets.QLabel(_translate("U", """使用前须知:
1、Qemu 跨架构效率较低,如果有条件建议优先使用 box86、exagear 等效率较高的转换层;
2、使用此方案需要使用到 Root 权限(需开启管理员模式)并安装 qemu-user-static;
3、chroot 时候可能会出现问题导致程序闪退或异常,出现该问题重启电脑即可;
4、在此环境使用 Wine 时,只能读取到您用户目录或本程序文件夹下的文件,其它路径无法读取;
5、移除容器时请保证在这次打开电脑时没有调用过需要删除容器,如果有调用过建议重启电脑后再移除;
6、暂时属于测试功能;""")))
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.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", "Github 源(推荐)"))
self.ipv6Action = QtWidgets.QAction(_translate("MainWindow", "备用源(只支持 IPv6 用户)"))
self.localAction = QtWidgets.QAction(_translate("MainWindow", "本地测试源(127.0.0.1)"))
self.changeSources.addAction(self.gitlinkAction)
self.changeSources.addAction(self.ipv6Action)
self.changeSources.addAction(self.localAction)
for i in [self.gitlinkAction, self.ipv6Action, self.localAction]:
i.setCheckable(True)
self.gitlinkAction.setChecked(True)
self.changeSourcesGroup = QtWidgets.QActionGroup(MainWindow)
self.changeSourcesGroup.addAction(self.gitlinkAction)
self.changeSourcesGroup.addAction(self.ipv6Action)
self.changeSourcesGroup.addAction(self.localAction)
self.retranslateUi(MainWindow)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
def retranslateUi(self, MainWindow):
_translate = QtCore.QCoreApplication.translate
MainWindow.setWindowTitle(_translate("MainWindow", "下载 Qemu 镜像"))
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"))
def ReadLocalInformation():
try:
global localJsonList
file = open(f"{homePath}/.deepin-wine-runner-ubuntu-images/lists.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"{homePath}/.deepin-wine-runner-ubuntu-images/lists.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", os.getenv("~"), "wine(wine);;wine64(wine64);;全部文件(*.*)")
if path[0] == "" or not path[1]:
return
try:
# 写入配置文件
rfile = open(f"{homePath}/.deepin-wine-runner-ubuntu-images/winelist.json", "r")
list = json.loads(rfile.read())
rfile.close()
# 创建映射
name = os.path.basename(os.path.dirname(os.path.dirname(path[0])))
if name == "" or name == None:
name = f"useradd-wine-{random.randint(0, 99999)}"
#binPath = os.path.dirname(os.path.dirname(path[0]))
os.makedirs(f"{programPath}/{name}/bin")
if os.system(f"ln -s '{path[0]}' '{programPath}/{name}/bin/wine'") != 0:
QtWidgets.QMessageBox.critical(window, "新建wine映射失败")
# C++ 版注释:不直接用 readwrite 是因为不能覆盖写入
file = open(f"{homePath}/.deepin-wine-runner-ubuntu-images/lists.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.gitlinkAction, ui.ipv6Action, 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:
internetJsonList = json.loads(requests.get(f"{internetWineSource}/lists.json").text)
internetJsonListNew = []
for k in internetJsonList:
archList = json.loads(requests.get(f"{internetWineSource}/{k}/lists.json").text)
for e in archList:
internetJsonListNew.append([e[0], e[1], k])
internetJsonList = internetJsonListNew
except:
traceback.print_exc()
QtWidgets.QMessageBox.critical(window, "错误", "无法连接服务器!")
return
print(internetJsonList)
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"{homePath}/.deepin-wine-runner-ubuntu-images/lists.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}"
choose = ui.internetWineList.currentIndex().row()
arch = internetJsonList[choose][2]
savePath = f"{homePath}/.deepin-wine-runner-ubuntu-images/{arch}/{self.fileSaveName}"
if not os.path.exists(os.path.dirname(savePath)):
os.makedirs(os.path.dirname(savePath))
# 文件下载
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))
self.ChangeDialog.emit(self.dialog, 100, 100, 100)
# 写入配置文件
rfile = open(f"{homePath}/.deepin-wine-runner-ubuntu-images/lists.json", "r")
list = json.loads(rfile.read())
rfile.close()
# C++ 版注释:不直接用 readwrite 是因为不能覆盖写入
file = open(f"{homePath}/.deepin-wine-runner-ubuntu-images/lists.json", "w")
list.append(self.fileSaveName.replace(".tar.gz", ""))
file.write(json.dumps(list))
file.close()
# 读取配置文件
self.ReadLocalInformation()
self.localJsonList = list
# 解压文件
shellCommand = ""
path = f"{homePath}/.deepin-wine-runner-ubuntu-images/{arch}/{self.fileSaveName.replace('.tar.gz', '')}/"
#path = f"{programPath}/{self.fileSaveName.replace('.7z', '')}"
shellCommand += f"""#!/bin/bash
mkdir -p \"{path}\"
tar -xvf \"{savePath}\" -C \"{path}\"
rm \"{savePath}\"
"""
#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())
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.replace(".tar.gz", "") == internetJsonList[choose][0]:
QtWidgets.QMessageBox.information(window, "提示", "您已经安装了这个镜像了!无需重复安装!")
return
#if(ui.deleteZip.isChecked() + ui.unzip.isChecked() == 2):
# ui.deleteZip.setChecked(False)
# ui.unzip.setChecked(False)
arch = internetJsonList[choose][2]
if "://" in downloadName:
downloadUrl = downloadName
else:
downloadUrl = f"{internetWineSource}/{arch}/{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]}”")
QT.thread = DownloadThread(
dialog,
downloadUrl,
"",
internetJsonList[choose][1],
ui.localWineList,
False,
not True,
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 os.path.exists("/tmp/deepin-wine-runner-qemu-lock"):
QtWidgets.QMessageBox.question(window, "提示", "检测到您的电脑已经运行了 Qemu/Chroot 容器,请重启后再移除")
return
if QtWidgets.QMessageBox.question(window, "提示", "你确定要删除吗?") == QtWidgets.QMessageBox.No:
return
if ui.localWineList.currentIndex().row() < 0:
QtWidgets.QMessageBox.information(window, "提示", "您未选择任何项")
return
try:
# {localJsonList[ui.localWineList.currentIndex().row()]}
path = f"{homePath}/.deepin-wine-runner-ubuntu-images/"
changed = False
for i in os.listdir(path):
if os.path.exists(f"{path}/{i}/{localJsonList[ui.localWineList.currentIndex().row()]}"):
changed = True
name = f"{path}/{i}/{localJsonList[ui.localWineList.currentIndex().row()]}".replace(".tar.gz", "")
if not changed:
name = f"{path}/i386/{localJsonList[ui.localWineList.currentIndex().row()]}".replace(".tar.gz", "")
print(name)
# 必须取消挂载目录才行
os.system(f"bash '{programPath}/UnMount.sh' '{name}'")
#name = f"{homePath}/.deepin-wine-runner-ubuntu-images/{localJsonList[ui.localWineList.currentIndex().row()]}"
dir = QtCore.QDir(name)
dir.removeRecursively()
QtCore.QFile.remove(name + ".tar.gz")
del localJsonList[ui.localWineList.currentIndex().row()]
file = open(f"{homePath}/.deepin-wine-runner-ubuntu-images/lists.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 get_now_lang()->"获取当前语言":
return os.getenv('LANG')
if __name__ == "__main__":
homePath = os.getenv("HOME")
localJsonList = []
internetJsonList = []
internetWineSourceList = [
"http://chroot.gfdgdxi.top",
"http://gfdgdxi.msns.cn/deepin-wine-runner-ubuntu-image/Sandbox", # 备用源,纯 IPv6 源
"http://127.0.0.1/deepin-wine-runner-ubuntu-image/Sandbox/" # 本地测试源
]
internetWineSource = internetWineSourceList[0]
app = QtWidgets.QApplication(sys.argv)
if os.system("which qemu-i386-static"):
if QtWidgets.QMessageBox.question(None, "提示", "检测到您未安装 qemu-user-static,是否安装?") == QtWidgets.QMessageBox.Yes:
OpenTerminal(f"pkexec bash '{programPath}/ShellList/InstallQemuUserStatic.sh'")
exit()
try:
if not os.path.exists(f"{homePath}/.deepin-wine-runner-ubuntu-images/"):
os.makedirs(f"{homePath}/.deepin-wine-runner-ubuntu-images/")
except:
traceback.print_exc()
QtWidgets.QMessageBox.critical(None, "错误", traceback.format_exc())
exit()
# 读取翻译
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()
# 连接信号
ui.addButton.clicked.connect(on_addButton_clicked)
ui.delButton.clicked.connect(on_delButton_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_()