#!/usr/bin/env python3
# 使用系统默认的 python3 运行
#################################################################################################################
# 作者:gfdgd xi、为什么您不喜欢熊出没和阿布呢
# 版本:3.0.0
# 更新时间:2022年12月10日
# 感谢:感谢 wine、deepin-wine 以及星火团队,提供了 wine、deepin-wine、spark-wine-devel 给大家使用,让我能做这个程序
# 基于 Python3 的 PyQt5 构建
#################################################################################################################
#################
# 引入所需的库
#################
import os
import sys
import time
import json
import random
import base64
import shutil
import hashlib
import platform
import threading
import traceback
import webbrowser
import subprocess
import req as requests
import urllib.parse as parse
import PyQt5.QtGui as QtGui
import PyQt5.QtCore as QtCore
import PyQt5.QtWidgets as QtWidgets
from trans import *
from Model import *
def PythonLower():
app = QtWidgets.QApplication(sys.argv)
QtWidgets.QMessageBox.critical(None, "错误", "Python 至少需要 3.6 及以上版本,目前版本:" + platform.python_version() + "")
sys.exit(1)
# Python 版本检测,因为 f-string 格式化要至少 Python 3.6 及以上的版本,所以需要检测
# 判断主版本号
if sys.version_info[0] < 3:
PythonLower()
if sys.version_info[1] < 6:
PythonLower()
###################
# 程序所需事件
###################
# 打开程序官网
def OpenProgramURL():
webbrowser.open_new_tab(programUrl)
# 读取文本文档
def readtxt(path):
f = open(path, "r") # 设置文件对象
str = f.read() # 获取内容
f.close() # 关闭文本对象
return str # 返回结果
# 写入文本文档
def write_txt(path, things):
file = open(path, 'w', encoding='UTF-8') # 设置文件对象
file.write(things) # 写入文本
file.close() # 关闭文本对象
# 获取用户桌面目录
def get_desktop_path():
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 # 返回目录
# 获取用户主目录
def get_home():
return os.path.expanduser('~')
# 第一个浏览按钮事件
def liulanbutton():
path = QtWidgets.QFileDialog.getExistingDirectory(widget, "选择 wine 容器", json.loads(readtxt(get_home() + "/.config/deepin-wine-runner/WineBotton.json"))["path"])
if path != "" and path != "()":
e1.setEditText(path)
write_txt(get_home() + "/.config/deepin-wine-runner/WineBotton.json", json.dumps({"path": path})) # 写入配置文件
# 第二个浏览按钮事件
def liulanexebutton():
path = QtWidgets.QFileDialog.getOpenFileName(widget, "选择 exe 可执行文件", json.loads(readtxt(get_home() + "/.config/deepin-wine-runner/FindExe.json"))["path"], "exe 可执行文件(*.exe);;MSI 文件(*.msi);;所有文件(*.*)")
if path != "" and path != "()":
e2.setEditText(path[0]) # 显示路径
write_txt(get_home() + "/.config/deepin-wine-runner/FindExe.json", json.dumps({"path": os.path.dirname(path[0])})) # 写入配置文件
run = None
# 使用多线程运行可执行文件
def runexebutton(self):
global run
DisableButton(True)
if not CheckProgramIsInstall(wine[o1.currentText()]) and not o1.currentText() in untipsWine:
if QtWidgets.QMessageBox.question(widget, "提示", "检查到您未安装这个 wine,是否继续使用这个 wine 运行?") == QtWidgets.QMessageBox.No:
DisableButton(False)
return
if e2.currentText() == "": # 判断文本框是否有内容
QtWidgets.QMessageBox.information(widget, "提示", "没有填写需要使用的 exe 应用")
DisableButton(False)
return
returnText.setText("")
run = Runexebutton_threading()
run.signal.connect(QT.ShowWineReturn)
run.showHistory.connect(QT.ShowHistory)
run.start()
class QT:
message = None
def ShowWineReturn(things):
returnText.insertPlainText(things)
def ShowHistory(temp):
e1.clear()
e2.clear()
e2.addItems(wineBottonHistory)
e2.setEditText(wineBottonHistory[-1])
e1.addItems(findExeHistory)
e1.setEditText(findExeHistory[-1])
repairList = []
class LogThreading(QtCore.QThread):
done = QtCore.pyqtSignal(list)
def __init__(self):
super().__init__()
def run(self):
global logText
repairList = []
logText = returnText.toPlainText()
print(logText.splitlines())
for i in logText.splitlines():
print(i)
checkingText = i.lower()
if "err:module:import_dll Library".lower() in checkingText:
# Lose Dll
repairList.append([1, i[i.index("Library") + 8: i.index("(")].strip()])
continue
if "err:module:fixup_imports_ilonly".lower() in checkingText:
# Lose Dll
repairList.append([1, i[i.index("_ilonly") + 8: i.index("not")].strip()])
continue
if "Cannot open assembly".lower() in checkingText and ": File does not contain a valid CIL image.".lower() in checkingText:
# Mono
repairList.append([2, i.replace(": File does not contain a valid CIL image.", "").replace("Cannot open assembly", "").strip()[1: -1]])
if "Could not load wine-gecko. HTML rendering will be disabled.".lower() in checkingText and "Could not find Wine Gecko. HTML rendering will be disabled.".lower() in checkingText:
# Disbled Gecko
repairList.append([3, ""])
if "Your wineserver binary was not upgraded correctly".lower() in checkingText:
repairList.append([4, ""])
if "Wine Mono is not installed".lower() in checkingText:
repairList.append([5, ""])
self.done.emit(repairList)
def DisableButton(things):
button_r_6.setDisabled(things)
button1.setDisabled(things)
button2.setDisabled(things)
button3.setDisabled(things)
wineConfig.setDisabled(things)
e1.setDisabled(things)
e2.setDisabled(things)
o1.setDisabled(things)
def CheckProgramIsInstall(program):
return not bool(os.system(f"which '{program}'"))
class Runexebutton_threading(QtCore.QThread):
signal = QtCore.pyqtSignal(str)
showHistory = QtCore.pyqtSignal(str)
def __init__(self):
super().__init__()
def run(self):
if e1.currentText() == "":
wineBottonPath = setting["DefultBotton"]
else:
wineBottonPath = e1.currentText()
option = ""
if setting["Architecture"] != "Auto":
option += f"WINEARCH={setting['Architecture']} "
if setting["MonoGeckoInstaller"]:
option += f"WINEDLLOVERRIDES=\"mscoree,mshtml=\" "
if not setting["Debug"]:
option += "WINEDEBUG=-all "
else:
option += "WINEDEBUG=FIXME,ERR,WARN,TRACE,Message "
wineUsingOption = ""
exePath = e2.currentText()
if True:
fileName = [".exe"]
changePath = False
for i in fileName:
if i in exePath:
print(i)
print(exePath)
l = exePath.index(i)
exePath = f"{exePath[:l+4]}' {exePath[l+4:]} '"
print(l)
print(exePath)
changePath = True
break
#if not changePath and not os.path.exists(changePath):
if not changePath and not os.path.exists(exePath):
# 删除前后无用空格以防止出现问题
print(exePath)
exePath = exePath.strip()
l = exePath.index(" ")
exePath = f"{exePath[:l]}' {exePath[l:]} '"
print(l)
#print(i)
print(exePath)
if o1.currentText() == "基于 UOS exagear 的 deepin-wine6-stable" or o1.currentText() == "基于 UOS box86 的 deepin-wine6-stable":
wineUsingOption = ""
if o1.currentText() == "基于 UOS box86 的 deepin-wine6-stable" or o1.currentText() == "基于 UOS exagear 的 deepin-wine6-stable":
if not os.path.exists(f"{programPath}/dlls-arm"):
if os.system(f"7z x -y \"{programPath}/dlls-arm.7z\" -o\"{programPath}\""):
QtWidgets.QMessageBox.critical(widget, "错误", "无法解压资源")
return
os.remove(f"{programPath}/dlls-arm.7z")
if setting["TerminalOpen"]:
res = ""
if e2.currentText()[-4:] == ".msi" and os.path.exists(e2.currentText()):
OpenTerminal("env WINEPREFIX='" + wineBottonPath + "' " + option + wine[o1.currentText()] + " msiexec /i '" + e2.currentText() + "' " + setting["WineOption"])
elif e2.currentText()[-4:] == ".bat" and os.path.exists(e2.currentText()):
OpenTerminal("env WINEPREFIX='" + wineBottonPath + "' " + option + wine[o1.currentText()] + " wineconsole '" + e2.currentText() + "' " + setting["WineOption"])
else:
OpenTerminal("env WINEPREFIX='" + wineBottonPath + "' " + option + wine[o1.currentText()] + " '" + exePath + "' " + setting["WineOption"])
#res = subprocess.Popen([f"'{programPath}/launch.sh' deepin-terminal -C \"WINEPREFIX='" + wineBottonPath + "' " + option + wine[o1.currentText()] + " '" + e2.currentText() + "' " + setting["WineOption"] + "\" --keep-open" + wineUsingOption], shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
else:
if e2.currentText()[-4:] == ".msi" and os.path.exists(e2.currentText()):
res = subprocess.Popen(["WINEPREFIX='" + wineBottonPath + "' " + option + wine[o1.currentText()] + " msiexec /i '" + e2.currentText() + "' " + setting["WineOption"]], shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
elif e2.currentText()[-4:] == ".bat" and os.path.exists(e2.currentText()):
res = subprocess.Popen(["WINEPREFIX='" + wineBottonPath + "' " + option + wine[o1.currentText()] + " wineconsole '" + e2.currentText() + "' " + setting["WineOption"]], shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
else:
print(["WINEPREFIX='" + wineBottonPath + "' " + option + wine[o1.currentText()] + " '" + exePath + "' " + setting["WineOption"]])
res = subprocess.Popen(["WINEPREFIX='" + wineBottonPath + "' " + option + wine[o1.currentText()] + " '" + exePath + "' " + setting["WineOption"]], shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
# 实时读取程序返回
#
if not setting["TerminalOpen"]:
while res.poll() is None:
try:
text = res.stdout.readline().decode("utf8")
except:
text = ""
self.signal.emit(text)
print(text, end="")
if len(findExeHistory) == 0 or findExeHistory[-1] != wineBottonPath:
findExeHistory.append(wineBottonPath) # 将记录写进数组
write_txt(get_home() + "/.config/deepin-wine-runner/FindExeHistory.json", str(json.dumps(ListToDictionary(findExeHistory)))) # 将历史记录的数组转换为字典并写入
if len(wineBottonHistory) == 0 or wineBottonHistory[-1] != e2.currentText():
wineBottonHistory.append(e2.currentText()) # 将记录写进数组
write_txt(get_home() + "/.config/deepin-wine-runner/WineBottonHistory.json", str(json.dumps(ListToDictionary(wineBottonHistory)))) # 将历史记录的数组转换为字典并写入
self.showHistory.emit("")
# 针对 QQ、TIM 安装后不会生成 lnk 的问题,由程序读取以及自动创建
# 判断是否安装了 QQ/TIM
for i in iconListUnBuild:
if os.path.exists(i[1].replace("wineBottonPath", wineBottonPath)):
if not os.path.exists(f"{get_home()}/.local/share/applications/wine/{i[0]}-{os.path.basename(wineBottonPath)}.desktop"):
print("图标不存在,创建图标")
# 图标不存在
# 写入 .desktop 文件
try:
os.system(f"mkdir -p '{get_home()}/.local/share/applications/wine'")
name = i[0]
if setting["BuildByBottleName"]:
name = f"{i[0]}——{os.path.basename(wineBottonPath)}"
write_txt(f"{get_home()}/.local/share/applications/wine/{i[0]}-{os.path.basename(wineBottonPath)}.desktop", f'''[Desktop Entry]
Name={name}
Exec=env WINEPREFIX='{wineBottonPath}' {option} {wine[o1.currentText()]} '{i[1].replace("wineBottonPath", wineBottonPath)}' {setting["WineOption"]} {wineUsingOption}
Icon={programPath}/Icon/{i[0]}.svg
Type=Application
StartupNotify=true''')
except:
# 写入不进去就别写入了,当什么事情都没发生就行
traceback.print_exc()
DisableButton(False)
class Temp:
webWindow = None
def OpenUrl(url):
print(url.url())
# 判断是否可以使用小窗打开
if not bad:
Temp.webWindow = QtWidgets.QMainWindow()
web = QtWebEngineWidgets.QWebEngineView()
web.setUrl(url)
Temp.webWindow.setWindowTitle("浏览页面")
Temp.webWindow.setCentralWidget(web)
Temp.webWindow.setWindowIcon(QtGui.QIcon(iconPath))
Temp.webWindow.show()
return
webbrowser.open_new_tab(url.url())
#QtCore.QUrl().url()
# 显示“关于这个程序”窗口
def about_this_program()->"显示“关于这个程序”窗口":
global about
global title
global iconPath
QT.message = QtWidgets.QMainWindow()
QT.message.setWindowIcon(QtGui.QIcon(iconPath))
messageWidget = QtWidgets.QWidget()
QT.message.setWindowTitle(f"关于 {title}")
messageLayout = QtWidgets.QGridLayout()
messageLayout.addWidget(QtWidgets.QLabel(f""), 0, 0, 1, 1, QtCore.Qt.AlignTop)
aboutInfo = QtWidgets.QTextBrowser(messageWidget)
aboutInfo.setFocusPolicy(QtCore.Qt.NoFocus)
#aboutInfo.copyAvailable.connect(lambda: print("b"))
aboutInfo.anchorClicked.connect(OpenUrl)
aboutInfo.setOpenLinks(False)
aboutInfo.setHtml(about)
aboutInfo.setOpenExternalLinks(False)
messageLayout.addWidget(aboutInfo, 0, 1, 1, 1)
ok = QtWidgets.QPushButton(transla.transe("U", "确定"))
ok.clicked.connect(QT.message.close)
messageLayout.addWidget(ok, 1, 1, 1, 1, QtCore.Qt.AlignBottom | QtCore.Qt.AlignRight)
messageWidget.setLayout(messageLayout)
QT.message.setCentralWidget(messageWidget)
QT.message.resize(int(messageWidget.frameGeometry().width() * 1.5), int(messageWidget.frameGeometry().height() * 1.5))
QT.message.show()
# 生成 desktop 文件在启动器
def make_desktop_on_launcher():
try:
if combobox1.currentText() == "" or e2.currentText() == "": # 判断文本框是否有内容
QtWidgets.QMessageBox.information(widget, "提示", "没有填写需要使用 exe 应用或保存的文件名")
return
if not CheckProgramIsInstall(wine[o1.currentText()]) and not o1.currentText() in untipsWine:
if QtWidgets.QMessageBox.question(widget, "提示", "检查到您未安装这个 wine,是否继续使用这个 wine 写入?") == QtWidgets.QMessageBox.No:
DisableButton(False)
return
else: # 如果都有
if os.path.exists(get_home() + "/.local/share/applications/" + combobox1.currentText() + ".desktop"): # 判断目录是否有该文件,如果有
choose = QtWidgets.QMessageBox.question(widget, "提示", "文件已经存在,是否覆盖?") == QtWidgets.QMessageBox.Yes
if choose: # 如要覆盖
os.remove(get_home() + "/.local/share/applications/" + combobox1.currentText() + ".desktop") # 删除该文件
else: # 如不覆盖
return # 结束
if e1.currentText() == "":
wineBottonPath = setting["DefultBotton"]
else:
wineBottonPath = e1.currentText()
option = ""
if setting["Architecture"] != "Auto":
option += f"WINEARCH={setting['Architecture']} "
if not setting["Debug"]:
option += "WINEDEBUG=-all "
else:
option += "WINEDEBUG=FIXME,ERR,WARN,TRACE,Message "
wineUsingOption = ""
if o1.currentText() == "基于 UOS box86 的 deepin-wine6-stable" or o1.currentText() == "基于 UOS exagear 的 deepin-wine6-stable":
if not os.path.exists(f"{programPath}/dlls-arm"):
if os.system(f"7z x -y \"{programPath}/dlls-arm.7z\" -o\"{programPath}\""):
QtWidgets.QMessageBox.critical(widget, "错误", "无法解压资源")
return
os.remove(f"{programPath}/dlls-arm.7z")
if o1.currentText() == "基于 UOS exagear 的 deepin-wine6-stable" or o1.currentText() == "基于 UOS box86 的 deepin-wine6-stable":
wineUsingOption = ""
value = ""
if e2.currentText()[:2].upper() == "C:":
value = f"{wineBottonPath}/drive_c/{e2.currentText()[2:]}".replace("\\", "/").replace("//", "/")
print(value)
iconPaths = iconPath
for i in iconList:
listValue = i[1].replace("wineBottonPath", wineBottonPath)
if listValue == e2.currentText() or listValue == value:
# 如果路径相同,即可以用程序对应的图标
iconPaths = f"{programPath}/Icon/{i[0]}.svg"
# 读到了就不需要再读取了
break
os.system(f"mkdir -p '{get_home()}/.local/share/applications/wine'")
write_txt(get_home() + "/.local/share/applications/wine/" + combobox1.currentText() + ".desktop", f'''[Desktop Entry]
Name={combobox1.currentText()}
Exec=env WINEPREFIX='{wineBottonPath}' {option} {wine[o1.currentText()]} '{e2.currentText()}' {setting["WineOption"]} {wineUsingOption}
Icon={iconPaths}
Type=Application
StartupNotify=true''') # 写入文本文档
if len(shellHistory) == 0 or shellHistory[-1] != combobox1.currentText():
shellHistory.append(combobox1.currentText()) # 将记录写进数组
write_txt(get_home() + "/.config/deepin-wine-runner/ShellHistory.json", str(json.dumps(ListToDictionary(shellHistory)))) # 将历史记录的数组转换为字典并写入
combobox1.clear()
combobox1.addItems(shellHistory)
QtWidgets.QMessageBox.information(widget, "提示", "生成完成!") # 显示完成对话框
except:
traceback.print_exc()
QtWidgets.QMessageBox.critical(widget, "错误", f"快捷方式创建失败,错误如下:\n{traceback.format_exc()}")
def ConfigQemu():
lists = []
for i in qemuBottleList:
lists.append(f"{i[0]}/{i[1]}")
choose = QtWidgets.QInputDialog.getItem(window, "提示", "选择需要 Chroot 到里面的容器", lists, 0, False)
if not choose[1]:
return
threading.Thread(target=OpenTerminal, args=[f"python3 '{programPath}/QemuRun.py' '{choose[0]}' '{int(setting['QemuUnMountHome'])}' "]).start()
print(choose)
# 生成 desktop 文件在桌面
# (第四个按钮的事件)
def make_desktop_on_desktop():
try:
if combobox1.currentText() == "" or e2.currentText() == "": # 判断文本框是否有内容
QtWidgets.QMessageBox.information(widget, "提示", "没有填写需要使用 exe 应用或保存的文件名")
return
if not CheckProgramIsInstall(wine[o1.currentText()]) and not o1.currentText() in untipsWine:
if QtWidgets.QMessageBox.question(widget, "提示", "检查到您未安装这个 wine,是否继续使用这个 wine 写入?") == QtWidgets.QMessageBox.No:
DisableButton(False)
return
else: # 如果都有
if os.path.exists(get_desktop_path() + "/" + combobox1.currentText() + ".desktop"): # 判断目录是否有该文件,如果有
choose = QtWidgets.QMessageBox.question(widget, "提示", "文件已经存在,是否覆盖?") == QtWidgets.QMessageBox.Yes
if choose: # 如要覆盖
os.remove(get_desktop_path() + "/" + combobox1.currentText() + ".desktop") # 删除该文件
else: # 如不覆盖
return # 结束
if e1.currentText() == "":
wineBottonPath = setting["DefultBotton"]
else:
wineBottonPath = e1.currentText()
wineUsingOption = ""
if o1.currentText() == "基于 UOS exagear 的 deepin-wine6-stable" or o1.currentText() == "基于 UOS box86 的 deepin-wine6-stable":
wineUsingOption = ""
if o1.currentText() == "基于 UOS box86 的 deepin-wine6-stable" or o1.currentText() == "基于 UOS exagear 的 deepin-wine6-stable":
if not os.path.exists(f"{programPath}/dlls-arm"):
if os.system(f"7z x -y \"{programPath}/dlls-arm.7z\" -o\"{programPath}\""):
QtWidgets.QMessageBox.critical(widget, "错误", "无法解压资源")
return
os.remove(f"{programPath}/dlls-arm.7z")
if not os.path.exists(get_desktop_path()):
os.makedirs(get_home())
os.mknod(get_desktop_path() + "/" + combobox1.currentText() + ".desktop")
option = ""
if setting["Architecture"] != "Auto":
option += f"WINEARCH={setting['Architecture']} "
if not setting["Debug"]:
option += "WINEDEBUG=-all "
value = ""
if e2.currentText()[:2].upper() == "C:":
value = f"{wineBottonPath}/drive_c/{e2.currentText()[2:]}".replace("\\", "/").replace("//", "/")
print(value)
iconPaths = iconPath
for i in iconList:
listValue = i[1].replace("wineBottonPath", wineBottonPath)
if listValue == e2.currentText() or listValue == value:
# 如果路径相同,即可以用程序对应的图标
iconPaths = f"{programPath}/Icon/{i[0]}.svg"
# 读到了就不需要再读取了
break
os.system(f"mkdir -p '{get_home()}/.local/share/applications/wine'")
write_txt(get_desktop_path() + "/" + combobox1.currentText() + ".desktop", f'''[Desktop Entry]
Name={combobox1.currentText()}
Exec=env WINEPREFIX='{wineBottonPath}' {option} {wine[o1.currentText()]} '{e2.currentText()}' {setting["WineOption"]} {wineUsingOption}
Icon={iconPaths}
Type=Application
StartupNotify=true''') # 写入文本文档
if len(shellHistory) == 0 or shellHistory[-1] != combobox1.currentText():
shellHistory.append(combobox1.currentText()) # 将记录写进数组
write_txt(get_home() + "/.config/deepin-wine-runner/ShellHistory.json", str(json.dumps(ListToDictionary(shellHistory)))) # 将历史记录的数组转换为字典并写入
combobox1.clear()
combobox1.addItems(shellHistory)
QtWidgets.QMessageBox.information(widget, "提示", "生成完成!") # 显示完成对话框
except:
traceback.print_exc()
QtWidgets.QMessageBox.critical(widget, "错误", f"快捷方式创建失败,错误如下:\n{traceback.format_exc()}")
# 数组转字典
def ListToDictionary(list):
dictionary = {}
for i in range(len(list)):
dictionary[i] = list[i]
return dictionary
def CleanProgramHistory():
if QtWidgets.QMessageBox.question(widget, "警告", "删除后将无法恢复,你确定吗?\n删除后软件将会自动重启。") == QtWidgets.QMessageBox.Yes:
try:
shutil.rmtree(get_home() + "/.config/deepin-wine-runner")
except:
traceback.print_exc()
QtWidgets.QMessageBox.critical(widget, "错误", traceback.format_exc())
ReStartProgram()
def CleanProgramCache():
try:
shutil.rmtree(get_home() + "/.cache/deepin-wine-runner")
QtWidgets.QMessageBox.information(widget, "提示", "缓存清理完毕!")
except:
traceback.print_exc()
QtWidgets.QMessageBox.critical(widget, "错误", traceback.format_exc())
# 重启本应用程序
def ReStartProgram():
python = sys.executable
os.execl(python, python, * sys.argv)
def KillProgram():
os.system(f"killall {wine[o1.currentText()]} -9")
os.system("killall winedbg -9")
exeName = os.path.basename(e2.currentText())
os.system(f"killall {exeName} -9")
def OpenWineBotton():
if e1.currentText() == "":
wineBottonPath = setting["DefultBotton"]
else:
wineBottonPath = e1.currentText()
os.system("xdg-open \"" + wineBottonPath.replace("\'", "\\\'") + "\"")
class RunWineProgramThread(QtCore.QThread):
signal = QtCore.pyqtSignal(str)
showHistory = QtCore.pyqtSignal(str)
def __init__(self, wineProgram, history = False, Disbled = True):
super().__init__()
self.wineProgram = wineProgram
self.history = history
self.Disbled = Disbled
def run(self):
if e1.currentText() == "":
wineBottonPath = setting["DefultBotton"]
else:
wineBottonPath = e1.currentText()
option = ""
if setting["MonoGeckoInstaller"]:
option += f"WINEDLLOVERRIDES=\"mscoree,mshtml=\" "
if setting["Architecture"] != "Auto":
option += f"WINEARCH={setting['Architecture']} "
if not setting["Debug"]:
option += "WINEDEBUG=-all "
wineUsingOption = ""
if o1.currentText() == "基于 UOS exagear 的 deepin-wine6-stable":
os.system(f"'{programPath}/deepin-wine-runner-create-botton.py' '{wineBottonPath}'")
if o1.currentText() == "基于 UOS exagear 的 deepin-wine6-stable" or o1.currentText() == "基于 UOS box86 的 deepin-wine6-stable":
wineUsingOption = ""
if o1.currentText() == "基于 UOS box86 的 deepin-wine6-stable" or o1.currentText() == "基于 UOS exagear 的 deepin-wine6-stable":
if not os.path.exists(f"{programPath}/dlls-arm"):
if os.system(f"7z x -y \"{programPath}/dlls-arm.7z\" -o\"{programPath}\""):
QtWidgets.QMessageBox.critical(widget, "错误", "无法解压资源")
return
os.remove(f"{programPath}/dlls-arm.7z")
if setting["TerminalOpen"]:
res = ""
OpenTerminal(f"env WINEPREFIX='" + wineBottonPath + "' " + option + wine[o1.currentText()] + " '" + self.wineProgram + "' " + setting["WineOption"] + " " + wineUsingOption)
#res = subprocess.Popen([f"'{programPath}/launch.sh' deepin-terminal -C \"WINEPREFIX='" + wineBottonPath + "' " + option + wine[o1.currentText()] + " '" + self.wineProgram + "' " + setting["WineOption"] + " " + wineUsingOption + "\" --keep-open"], shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
else:
res = subprocess.Popen(["WINEPREFIX='" + wineBottonPath + "' " + option + wine[o1.currentText()] + " '" + self.wineProgram + "' " + setting["WineOption"]], shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
# 实时读取程序返回
if not setting["TerminalOpen"]:
while res.poll() is None:
try:
text = res.stdout.readline().decode("utf8")
except:
text = ""
self.signal.emit(text)
print(text, end="")
if self.history:
if len(findExeHistory) == 0 or findExeHistory[-1] != wineBottonPath:
findExeHistory.append(wineBottonPath) # 将记录写进数组
write_txt(get_home() + "/.config/deepin-wine-runner/FindExeHistory.json", str(json.dumps(ListToDictionary(findExeHistory)))) # 将历史记录的数组转换为字典并写入
if len(wineBottonHistory) == 0 or wineBottonHistory[-1] != e2.currentText():
wineBottonHistory.append(e2.currentText()) # 将记录写进数组
write_txt(get_home() + "/.config/deepin-wine-runner/WineBottonHistory.json", str(json.dumps(ListToDictionary(wineBottonHistory)))) # 将历史记录的数组转换为字典并写入
self.showHistory.emit("")
if self.Disbled:
DisableButton(False)
runProgram = None
def RunWineProgram(wineProgram, history = False, Disbled = True):
global runProgram
DisableButton(True)
if not CheckProgramIsInstall(wine[o1.currentText()]) and o1.currentText() != "基于 linglong 的 deepin-wine6-stable(不推荐)" and o1.currentText() != "基于 UOS exagear 的 deepin-wine6-stable" and o1.currentText() != "基于 UOS box86 的 deepin-wine6-stable":
if not CheckProgramIsInstall(wine[o1.currentText()]) and not o1.currentText() in untipsWine:
DisableButton(False)
return
returnText.setText("")
runProgram = RunWineProgramThread(wineProgram, history, Disbled)
runProgram.signal.connect(QT.ShowWineReturn)
runProgram.showHistory.connect(QT.ShowHistory)
runProgram.start()
def CleanWineBottonByUOS():
if e1.currentText() == "":
wineBottonPath = setting["DefultBotton"]
else:
wineBottonPath = e1.currentText()
OpenTerminal(f"env WINE='{wine[o1.currentText()]}' '{programPath}/cleanbottle.sh' '{wineBottonPath}'")
def FontAppStore():
if e1.currentText() == "":
wineBottonPath = setting["DefultBotton"]
else:
wineBottonPath = e1.currentText()
OpenTerminal(f"env WINE='{programPath}/launch.sh' '{programPath}/InstallFont.py' '{wineBottonPath}' {int(setting['RuntimeCache'])}")
def GetDllFromInternet():
if e1.currentText() == "":
wineBottonPath = setting["DefultBotton"]
else:
wineBottonPath = e1.currentText()
OpenTerminal(f"env WINE='{programPath}/launch.sh' '{programPath}/InstallDll.py' '{wineBottonPath}' '{wine[o1.currentText()]}' {int(setting['RuntimeCache'])}")
def DeleteWineBotton():
if QtWidgets.QMessageBox.question(widget, "提示", "你确定要删除容器吗?删除后将无法恢复!\n如果没有选择 wine 容器,将会自动删除默认的容器!") == QtWidgets.QMessageBox.No:
return
if e1.currentText() == "":
wineBottonPath = setting["DefultBotton"]
else:
wineBottonPath = e1.currentText()
try:
shutil.rmtree(wineBottonPath)
QtWidgets.QMessageBox.information(widget, "提示", "删除完毕!")
except:
traceback.print_exc()
QtWidgets.QMessageBox.critical(widget, "错误", traceback.format_exc())
def ThankWindow():
# 直接显示关于窗口,关于窗口已经添加
about_this_program()
def GetScreenSize():
screenInformation = []
# 使用 xrandr 进行筛选
for i in subprocess.getoutput("xrandr").split('\n'):
if not " connected " in i: # 检测连接的显示器
continue
# 获取分辨率基本信息,如
# DisplayPort-0 connected 1600x900+1280+0 (normal left inverted right x axis y axis) 434mm x 236mm
# 先判断是否为主屏幕
main = False
if "primary" in i:
main = True
# 进行进一步筛选
i = i[i.index("connected"):].replace("connected", "").replace("primary", "")
# 进行初步筛选,如
# 1600x900+1280+0 (normal left inverted right x axis y axis) 434mm x 236mm
i = i[:i.index("(")].replace(" ", "")
# 筛选为 1600x900+0+0 进行最后数值的提取
screenInformation.append([
int(i[:i.index("x")]), # 获取宽度
int(i[i.index("x") + 1 :i.index("+")]), # 获取高度
int(i[i.index("+") + 1:].split('+')[0]), # 获取显示屏 X 坐标
int(i[i.index("+") + 1:].split('+')[1]), # 获取显示屏 Y 坐标
main # 是否为主屏幕
])
return screenInformation # 返回结果
choose = None
class ProgramSetting():
wineBottonA = None
wineDebug = None
defultWine = None
defultBotton = None
terminalOpen = None
wineOption = None
#wineBottonDifferent = None
centerWindow = None
message = None
theme = None
monogeckoInstaller = None
autoWine = None
runtimeCache = None
buildByBottleName = None
autoPath = None
qemuUnmountHome = None
def ShowWindow():
ProgramSetting.message = QtWidgets.QMainWindow()
widget = QtWidgets.QWidget()
widgetLayout = QtWidgets.QGridLayout()
widgetLayout.addWidget(QtWidgets.QLabel(transla.transe("U", "选择 Wine 容器版本:")), 0, 0, 1, 1)
widgetLayout.addWidget(QtWidgets.QLabel(transla.transe("U", "wine DEBUG 信息输出:")), 1, 0, 1, 1)
widgetLayout.addWidget(QtWidgets.QLabel(transla.transe("U", "默认 Wine:")), 2, 0, 1, 1)
widgetLayout.addWidget(QtWidgets.QLabel(transla.transe("U", "默认 Wine 容器:")), 3, 0, 1, 1)
widgetLayout.addWidget(QtWidgets.QLabel(transla.transe("U", "使用终端打开:")), 4, 0, 1, 1)
widgetLayout.addWidget(QtWidgets.QLabel(transla.transe("U", "自定义 wine 参数:")), 5, 0, 1, 1)
widgetLayout.addWidget(QtWidgets.QLabel(transla.transe("U", "程序主题:")), 6, 0, 1, 1)
widgetLayout.addWidget(QtWidgets.QLabel(transla.transe("U", "Wine 默认 Mono 和 Gecko 安装器:")), 7, 0, 1, 1)
widgetLayout.addWidget(QtWidgets.QLabel(transla.transe("U", "忽略未安装的 Wine:")), 8, 0, 1, 1)
widgetLayout.addWidget(QtWidgets.QLabel(transla.transe("U", "下载缓存:")), 9, 0, 1, 1)
widgetLayout.addWidget(QtWidgets.QLabel(transla.transe("U", "图标生成:")), 10, 0, 1, 1)
widgetLayout.addWidget(QtWidgets.QLabel(transla.transe("U", "自动根据EXE名称生成路径:")), 11, 0, 1, 1)
widgetLayout.addWidget(QtWidgets.QLabel(transla.transe("U", "Qemu + Chroot 挂载用户目录:")), 12, 0, 1, 1)
ProgramSetting.wineBottonA = QtWidgets.QComboBox()
ProgramSetting.wineDebug = QtWidgets.QCheckBox(transla.transe("U", "开启 DEBUG 输出"))
ProgramSetting.defultWine = QtWidgets.QComboBox()
ProgramSetting.defultBotton = QtWidgets.QLineEdit()
ProgramSetting.theme = QtWidgets.QComboBox()
ProgramSetting.theme.addItems(QtWidgets.QStyleFactory.keys())
ProgramSetting.theme.setCurrentText(setting["Theme"])
save = QtWidgets.QPushButton(transla.transe("U", "保存"))
save.clicked.connect(ProgramSetting.Save)
defultBottonButton = QtWidgets.QPushButton(transla.transe("U", "浏览"))
defultBottonButton.clicked.connect(ProgramSetting.Browser)
themeTry = QtWidgets.QPushButton(transla.transe("U", "测试(重启后变回设置的主题)"))
themeTry.clicked.connect(ProgramSetting.Try)
ProgramSetting.terminalOpen = QtWidgets.QCheckBox(transla.transe("U", "使用终端打开(deepin 终端)"))
ProgramSetting.wineOption = QtWidgets.QLineEdit()
ProgramSetting.monogeckoInstaller = QtWidgets.QCheckBox(transla.transe("U", "屏蔽 Wine 默认 Mono 和 Gecko 安装器"))
ProgramSetting.autoWine = QtWidgets.QCheckBox(transla.transe("U", "不显示未检测到的 Wine"))
ProgramSetting.runtimeCache = QtWidgets.QCheckBox(transla.transe("U", "开启下载缓存"))
ProgramSetting.buildByBottleName = QtWidgets.QCheckBox(transla.transe("U", "本软件构建的图标后面添加容器名"))
ProgramSetting.autoPath = QtWidgets.QCheckBox(transla.transe("U", "自动根据文件名生成容器路径(开启后必须通过修改默认wine容器路径才可指定其它路径,重启程序后生效)"))
ProgramSetting.qemuUnmountHome = QtWidgets.QCheckBox(transla.transe("U", "使用 Qemu + Chroot 时不挂载用户目录并与系统隔离(修改后重启操作系统生效)"))
ProgramSetting.wineBottonA.addItems(["Auto", "win32", "win64"])
ProgramSetting.wineBottonA.setCurrentText(setting["Architecture"])
ProgramSetting.wineDebug.setChecked(setting["Debug"])
ProgramSetting.defultWine.addItems(wine.keys())
ProgramSetting.defultWine.setCurrentText(setting["DefultWine"])
ProgramSetting.defultBotton.setText(setting["DefultBotton"])
ProgramSetting.terminalOpen.setChecked(setting["TerminalOpen"])
ProgramSetting.wineOption.setText(setting["WineOption"])
ProgramSetting.monogeckoInstaller.setChecked(setting["MonoGeckoInstaller"])
ProgramSetting.autoWine.setChecked(setting["AutoWine"])
ProgramSetting.runtimeCache.setChecked(setting["RuntimeCache"])
ProgramSetting.buildByBottleName.setChecked(setting["BuildByBottleName"])
ProgramSetting.autoPath.setChecked(setting["AutoPath"])
ProgramSetting.qemuUnmountHome.setChecked(setting["QemuUnMountHome"])
# QemuUnMountHome
widgetLayout.addWidget(ProgramSetting.wineBottonA, 0, 1, 1, 1)
widgetLayout.addWidget(ProgramSetting.wineDebug, 1, 1, 1, 1)
widgetLayout.addWidget(ProgramSetting.defultWine, 2, 1, 1, 1)
widgetLayout.addWidget(ProgramSetting.defultBotton, 3, 1, 1, 1)
widgetLayout.addWidget(defultBottonButton, 3, 2, 1, 1)
widgetLayout.addWidget(ProgramSetting.terminalOpen, 4, 1, 1, 1)
widgetLayout.addWidget(ProgramSetting.wineOption, 5, 1, 1, 1)
widgetLayout.addWidget(ProgramSetting.theme, 6, 1, 1, 1)
widgetLayout.addWidget(themeTry, 6, 2, 1, 1)
widgetLayout.addWidget(ProgramSetting.monogeckoInstaller, 7, 1, 1, 1)
widgetLayout.addWidget(ProgramSetting.autoWine, 8, 1, 1, 1)
widgetLayout.addWidget(ProgramSetting.runtimeCache, 9, 1, 1, 1)
widgetLayout.addWidget(ProgramSetting.buildByBottleName, 10, 1, 1, 1)
widgetLayout.addWidget(ProgramSetting.autoPath, 11, 1, 1, 2)
widgetLayout.addWidget(ProgramSetting.qemuUnmountHome, 12, 1, 1, 2)
widgetLayout.addWidget(save, 13, 2, 1, 1)
widget.setLayout(widgetLayout)
ProgramSetting.message.setCentralWidget(widget)
ProgramSetting.message.setWindowIcon(QtGui.QIcon(iconPath))
ProgramSetting.message.setWindowTitle(f"设置 wine 运行器 {version}")
ProgramSetting.message.show()
def Browser():
path = QtWidgets.QFileDialog.getExistingDirectory(ProgramSetting.message, transla.transe("U", "选择 Wine 容器"), json.loads(readtxt(get_home() + "/.config/deepin-wine-runner/WineBotton.json"))["path"])
if path == "" or path == None or path == "()" or path == ():
return
ProgramSetting.defultBotton.setText(path)
def Try():
app.setStyle(QtWidgets.QStyleFactory.create(ProgramSetting.theme.currentText()))
def Save():
# 写入容器位数设置
setting["Architecture"] = ProgramSetting.wineBottonA.currentText()
setting["Debug"] = ProgramSetting.wineDebug.isChecked()
setting["DefultWine"] = ProgramSetting.defultWine.currentText()
setting["DefultBotton"] = ProgramSetting.defultBotton.text()
setting["TerminalOpen"] = ProgramSetting.terminalOpen.isChecked()
setting["WineOption"] = ProgramSetting.wineOption.text()
setting["Theme"] = ProgramSetting.theme.currentText()
setting["MonoGeckoInstaller"] = ProgramSetting.monogeckoInstaller.isChecked()
setting["AutoWine"] = ProgramSetting.autoWine.isChecked()
setting["RuntimeCache"] = ProgramSetting.runtimeCache.isChecked()
setting["BuildByBottleName"] = ProgramSetting.buildByBottleName.isChecked()
setting["AutoPath"] = ProgramSetting.autoPath.isChecked()
setting["QemuUnMountHome"] = ProgramSetting.qemuUnmountHome.isChecked()
try:
write_txt(get_home() + "/.config/deepin-wine-runner/WineSetting.json", json.dumps(setting))
except:
traceback.print_exc()
QtWidgets.QMessageBox.critical(ProgramSetting.message, "错误", traceback.format_exc())
return
QtWidgets.QMessageBox.information(ProgramSetting.message, "提示", "保存完毕!")
def ChangePath():
e1.setCurrentText(f'{setting["DefultBotton"]}/{os.path.splitext(os.path.basename(e2.currentText()))[0]}')
def getFileFolderSize(fileOrFolderPath):
"""get size for file or folder"""
totalSize = 0
try:
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
except:
return totalSize
# 获取当前语言
def get_now_lang()->"获取当前语言":
return os.getenv('LANG')
# 又需要修复多线程导致的控件问题
def AddDockerMenu():
global dockers
global openFileManager
global openTerminal
dockers = menu.addMenu("该 Docker 基础管理")
openFileManager = QtWidgets.QAction("打开默认文件管理器")
openTerminal = QtWidgets.QAction("打开默认终端")
openFileManager.triggered.connect(lambda: threading.Thread(target=os.system, args=[f"xdg-open '{get_home()}'"]).start())
openTerminal.triggered.connect(lambda: threading.Thread(target=os.system, args=[f"x-terminal-emulator"]).start())
dockers.addAction(openFileManager)
dockers.addAction(openTerminal)
newPackage = False
class GetVersionThread(QtCore.QThread):
signal = QtCore.pyqtSignal(str)
def __init__(self) -> None:
super().__init__()
def run(self):
global about
global window
global newPackage
global programVersionType
# 目前分为几个版本(在 control 文件区分):
# 星火版本:~spark
# 商店版本:~uos
# 编译版本:无版本号
# Gitee/Github……:正常版本
# Docker 版本
programVersionTypeLnk = {
"spark": "星火应用商店版本",
"uos": "deepin/UOS 应用商店版本<带签名>"
}
# 直接判断是不是 Docker 版本
if os.path.exists(f"{programPath}/docker.txt") or os.path.exists("/.dockerenv"):
programVersionType = "Docker/Chroot 内置版本"
window.setWindowTitle(f"{title} (Docker/Chroot 内置版本)")
self.signal.emit("")
else:
programVersionType = "从源码运行的版本"
try:
if not os.path.exists("/var/lib/dpkg/status"):
print("无 dpkg,结束")
file = open("/var/lib/dpkg/status", "r")
fileName = file.read().splitlines()
package = False
for i in range(0, len(fileName)):
if fileName[i] == "Package: spark-deepin-wine-runner-docker":
programVersionType = "Docker 内置版本"
window.setWindowTitle(f"{title} (Docker 内置版本)")
#AddDockerMenu()
self.signal.emit("")
break
if fileName[i] == "Package: spark-deepin-wine-runner-52":
programVersionType = "吾爱专版"
window.setWindowTitle(f"{title}(吾爱专版)")
newPackage = False
break
if fileName[i] == "Package: spark-deepin-wine-runner":
package = True
newPackage = True
continue
if fileName[i] == "Package: wine-runner-linux":
package = True
continue
if not package:
continue
if fileName[i].replace(" ", "").replace("\n", "") == "":
# 空行,不再考虑
break
# 搜索版本号
try:
if fileName[i][:fileName[i].index(":")] == "Version":
version = fileName[i][fileName[i].index(":") + 1:].strip()
print(f"版本号为:{version}")
if not "-" in version:
programVersionType = "从Gitee/Github/Gitlink等平台获取的版本"
break
programVersionType = version[version.index("-") + 1:]
print(programVersionType)
if "-" in programVersionType:
# 考虑到如 2.1.0-2-spark 的情况
programVersionType = programVersionType[programVersionType.index("-") + 1:]
try:
programVersionType = programVersionTypeLnk[programVersionType]
except:
programVersionType = "从Gitee/Github/Gitlink等平台获取的版本"
break
except:
traceback.print_exc()
continue
except:
print("无法读取,当没有处理")
print(programVersionType)
about = about.replace("@VersionForType@", programVersionType)
# 获取程序体积
about = about.replace("@programSize@", str(int(getFileFolderSize(programPath) / 1024 / 1024)))
def GetVersion():
global runVersion
runVersion = GetVersionThread()
runVersion.signal.connect(AddDockerMenu)
runVersion.start()
def FindFile(file, name):
for i in os.listdir(file):
path = f"{file}/{i}"
if os.path.isdir(path):
returnPath = FindFile(path, name)
if returnPath != None:
return returnPath.replace("//", "/")
if os.path.isfile(path):
if i == name:
return path
return None
###########################
# 加载配置
###########################
defultProgramList = {
"Architecture": "Auto",
"Debug": True,
"DefultWine": "deepin-wine6 stable",
"DefultBotton" : get_home() + "/.wine",
"TerminalOpen": False,
"WineOption": "",
"WineBottonDifferent": False,
"CenterWindow": False,
"Theme": "",
"MonoGeckoInstaller": False,
"AutoWine": True,
"RuntimeCache": True,
"MustRead": False,
"BuildByBottleName": False,
"AutoPath": False,
"QemuUnMountHome": False
}
if not os.path.exists(get_home() + "/.config/"): # 如果没有配置文件夹
os.mkdir(get_home() + "/.config/") # 创建配置文件夹
if not os.path.exists(get_home() + "/.config/deepin-wine-runner"): # 如果没有配置文件夹
os.mkdir(get_home() + "/.config/deepin-wine-runner") # 创建配置文件夹
if not os.path.exists(get_home() + "/.config/deepin-wine-runner/ShellHistory.json"): # 如果没有配置文件
write_txt(get_home() + "/.config/deepin-wine-runner/ShellHistory.json", json.dumps({})) # 创建配置文件
if not os.path.exists(get_home() + "/.config/deepin-wine-runner/FindExeHistory.json"): # 如果没有配置文件
write_txt(get_home() + "/.config/deepin-wine-runner/FindExeHistory.json", json.dumps({})) # 创建配置文件
if not os.path.exists(get_home() + "/.config/deepin-wine-runner/WineBottonHistory.json"): # 如果没有配置文件
write_txt(get_home() + "/.config/deepin-wine-runner/WineBottonHistory.json", json.dumps({})) # 创建配置文件
if not os.path.exists(get_home() + "/.config/deepin-wine-runner/ISOPath.json"): # 如果没有配置文件
write_txt(get_home() + "/.config/deepin-wine-runner/ISOPath.json", json.dumps({})) # 写入(创建)一个配置文件
if not os.path.exists(get_home() + "/.config/deepin-wine-runner/ISOPathFound.json"): # 如果没有配置文件
write_txt(get_home() + "/.config/deepin-wine-runner/ISOPathFound.json", json.dumps({})) # 写入(创建)一个配置文件
if not os.path.exists(get_home() + "/.config/deepin-wine-runner/FindExe.json"): # 如果没有配置文件
write_txt(get_home() + "/.config/deepin-wine-runner/FindExe.json", json.dumps({"path": "~"})) # 写入(创建)一个配置文件
if not os.path.exists(get_home() + "/.config/deepin-wine-runner/FindISO.json"): # 如果没有配置文件
write_txt(get_home() + "/.config/deepin-wine-runner/FindISO.json", json.dumps({"path": "~"})) # 写入(创建)一个配置文件
if not os.path.exists(get_home() + "/.config/deepin-wine-runner/WineBotton.json"): # 如果没有配置文件
write_txt(get_home() + "/.config/deepin-wine-runner/WineBotton.json", json.dumps({"path": "~/.deepinwine"})) # 写入(创建)一个配置文件
if not os.path.exists(get_home() + "/.config/deepin-wine-runner/WineSetting.json"): # 如果没有配置文件
write_txt(get_home() + "/.config/deepin-wine-runner/WineSetting.json", json.dumps(defultProgramList)) # 写入(创建)一个配置文件
###########################
# 设置变量
###########################
programPath = os.path.split(os.path.realpath(__file__))[0] # 返回 string
# 如果要添加其他 wine,请使用安装更多 Wine 功能
#############
# 检测 Wine
#############
try:
wine = {
"基于 UOS box86 的 deepin-wine6-stable": f"WINEPREDLL='{programPath}/dlls-arm' WINEDLLPATH=/opt/deepin-wine6-stable/lib BOX86_NOSIGSEGV=1 /opt/deepin-box86/box86 /opt/deepin-wine6-stable/bin/wine ",
"基于 UOS exagear 的 deepin-wine6-stable": f"WINEPREDLL='{programPath}/dlls-arm' WINEDLLPATH=/opt/deepin-wine6-stable/lib /opt/exagear/bin/ubt_x64a64_al --path-prefix {get_home()}/.deepinwine/debian-buster --utmp-paths-list {get_home()}/.deepinwine/debian-buster/.exagear/utmp-list --vpaths-list {get_home()}/.deepinwine/debian-buster/.exagear/vpaths-list --opaths-list {get_home()}/.deepinwine/debian-buster/.exagear/opaths-list --smo-mode fbase --smo-severity smart --fd-limit 8192 --foreign-ubt-binary /opt/exagear/bin/ubt_x32a64_al -- /opt/deepin-wine6-stable/bin/wine ",
"deepin-wine6 stable": "deepin-wine6-stable",
"deepin-wine5 stable": "deepin-wine5-stable",
"spark-wine7-devel": "spark-wine7-devel",
"deepin-wine": "deepin-wine",
"deepin-wine5": "deepin-wine5",
"wine": "wine",
"wine64": "wine64",
"ukylin-wine": "ukylin-wine",
"mono(这不是 wine,但可以实现初步调用运行 .net 应用)": "mono",
"基于 linglong 的 deepin-wine6-stable(不推荐)": f"ll-cli run '' --exec '/bin/deepin-wine6-stable'"
}
untipsWine = ["基于 exagear 的 deepin-wine6-stable", "基于 UOS box86 的 deepin-wine6-stable", "基于 UOS exagear 的 deepin-wine6-stable", "基于 linglong 的 deepin-wine6-stable(不推荐)"]
canUseWine = []
if os.path.exists("/opt/deepin-box86/box86") and os.path.exists("/opt/deepin-wine6-stable/bin/wine"):
canUseWine.append("基于 UOS box86 的 deepin-wine6-stable")
if os.path.exists("/opt/exagear/bin/ubt_x64a64_al") and os.path.exists("/opt/deepin-wine6-stable/bin/wine"):
canUseWine.append("基于 UOS exagear 的 deepin-wine6-stable")
if not os.system("which exagear") and os.path.exists("/opt/deepin-wine6-stable/bin/wine"):
canUseWine.append("基于 exagear 的 deepin-wine6-stable")
for i in wine.keys():
if not os.system(f"which '{wine[i]}'"):
canUseWine.append(i)
if os.path.exists("/persistent/linglong/layers/"): # 判断是否使用 linglong
for i in os.listdir("/persistent/linglong/layers/"):
try:
dire = os.listdir(f"/persistent/linglong/layers/{i}")[-1]
arch = os.listdir(f"/persistent/linglong/layers/{i}/{dire}")[-1]
if os.path.exists(f"/persistent/linglong/layers/{i}/{dire}/{arch}/runtime/bin/deepin-wine6-stable"):
wine["基于 linglong 的 deepin-wine6-stable(不推荐)"] = f"ll-cli run {i} --exec '/bin/deepin-wine6-stable'"
canUseWine.append("基于 linglong 的 deepin-wine6-stable(不推荐)")
break
except:
pass
# 读取自定义安装的 Wine(需要解包的才能使用)
qemuBottleList = []
qemuPath = f"{get_home()}/.deepin-wine-runner-ubuntu-images"
if not os.system("which qemu-i386-static"):
if os.path.exists(qemuPath):
for g in os.listdir(qemuPath):
archPath = f"{qemuPath}/{g}"
arch = g
if os.path.isdir(archPath):
for d in os.listdir(archPath):
bottlePath = f"{archPath}/{d}"
if os.path.isdir(bottlePath):
qemuBottleList.append([
arch,
d,
bottlePath
])
shellHistory = list(json.loads(readtxt(get_home() + "/.config/deepin-wine-runner/ShellHistory.json")).values())
findExeHistory = list(json.loads(readtxt(get_home() + "/.config/deepin-wine-runner/FindExeHistory.json")).values())
wineBottonHistory = list(json.loads(readtxt(get_home() + "/.config/deepin-wine-runner/WineBottonHistory.json")).values())
isoPath = list(json.loads(readtxt(get_home() + "/.config/deepin-wine-runner/ISOPath.json")).values())
isoPathFound = list(json.loads(readtxt(get_home() + "/.config/deepin-wine-runner/ISOPathFound.json")).values())
setting = json.loads(readtxt(get_home() + "/.config/deepin-wine-runner/WineSetting.json"))
change = False
if not os.path.exists(get_home() + "/.config/deepin-wine-runner/mono-lock"):
os.mknod(f"{get_home()}/.config/deepin-wine-runner/mono-lock")
setting["MonoGeckoInstaller"] = False
change = True
for i in defultProgramList.keys():
if not i in setting:
change = True
setting[i] = defultProgramList[i]
if change:
write_txt(get_home() + "/.config/deepin-wine-runner/WineSetting.json", json.dumps(setting))
try:
# 不再从列表读取,直接读目录
for i in os.listdir(f"{programPath}/wine/"):
#for i in json.loads(readtxt(f"{programPath}/wine/winelist.json")):
if os.path.exists(f"{programPath}/wine/{i}") and os.path.isdir(f"{programPath}/wine/{i}"):
name = ""
qemuInstall = False
nameValue = [["", ""]]
try:
if os.path.exists("/opt/deepin-box86/box86"):
nameValue.append(
[
"基于 UOS box86 的 ",
f"WINEPREDLL='{programPath}/dlls-arm' WINEDLLPATH=/opt/deepin-wine6-stable/lib BOX86_NOSIGSEGV=1 /opt/deepin-box86/box86 "
]
)
if os.system("which box86") == 0:
nameValue.append(
[
"基于 box86 的 ",
f"box86 "
]
)
if os.system("which box64") == 0:
nameValue.append(
[
"基于 box64 的 ",
f"box64 "
]
)
if os.system("which exagear") == 0:
nameValue.append(
[
"基于 exagear 的 ",
f"exagear "
]
)
if os.path.exists("/opt/exagear/bin/ubt_x64a64_al"):
nameValue.append(
[
"基于 UOS exagear 的 ",
f"WINEPREDLL='{programPath}/dlls-arm' WINEDLLPATH=/opt/deepin-wine6-stable/lib /opt/exagear/bin/ubt_x64a64_al --path-prefix {get_home()}/.deepinwine/debian-buster --utmp-paths-list {get_home()}/.deepinwine/debian-buster/.exagear/utmp-list --vpaths-list {get_home()}/.deepinwine/debian-buster/.exagear/vpaths-list --opaths-list {get_home()}/.deepinwine/debian-buster/.exagear/opaths-list --smo-mode fbase --smo-severity smart --fd-limit 8192 --foreign-ubt-binary /opt/exagear/bin/ubt_x32a64_al -- "
]
)
for g in qemuBottleList:
nameValue.append([
f"使用qemu-{g[0]}-static 调用容器{g[1]}运行 ",
f"python3 '{programPath}/QemuRun.py' '{g[0]}/{g[1]}' {int(setting['QemuUnMountHome'])} "
])
except:
traceback.print_exc()
for k in nameValue:
print(k)
if "qemu" in k[0]:
chrootProgramPath = "/opt/apps/deepin-wine-runner"
else:
chrootProgramPath = programPath
if os.path.exists(f"{programPath}/wine/{i}/bin/wine"):
wine[f"{k[0]}{chrootProgramPath}/wine/{i}/bin/wine"] = f"{k[1]}{chrootProgramPath}/wine/{i}/bin/wine"
canUseWine.append(f"{k[0]}{chrootProgramPath}/wine/{i}/bin/wine")
untipsWine.append(f"{k[0]}{chrootProgramPath}/wine/{i}/bin/wine")
if os.path.exists(f"{programPath}/wine/{i}/bin/wine64"):
wine[f"{k[0]}{chrootProgramPath}/wine/{i}/bin/wine64"] = f"{k[1]}{chrootProgramPath}/wine/{i}/bin/wine64"
canUseWine.append(f"{k[0]}{chrootProgramPath}/wine/{i}/bin/wine64")
untipsWine.append(f"{k[0]}{chrootProgramPath}/wine/{i}/bin/wine64")
except:
pass
try:
for i in os.listdir(f"{get_home()}/.deepinwine/"):
if os.path.exists(f"{get_home()}/.deepinwine/{i}/bin/wine"):
wine[f"{get_home()}/.deepinwine/{i}/bin/wine"] = f"{get_home()}/.deepinwine/{i}/bin/wine"
canUseWine.append(f"{get_home()}/.deepinwine/{i}/bin/wine")
if os.path.exists(f"{get_home()}/.deepinwine/{i}/bin/wine64"):
wine[f"{get_home()}/.deepinwine/{i}/bin/wine64"] = f"{get_home()}/.deepinwine/{i}/bin/wine64"
canUseWine.append(f"{get_home()}/.deepinwine/{i}/bin/wine64")
except:
pass
except:
traceback.print_exc()
app = QtWidgets.QApplication(sys.argv)
QtWidgets.QMessageBox.critical(None, "错误", f"无法读取配置,无法继续\n{traceback.format_exc()}")
sys.exit(1)
# transla.transe
programVersionType = ""
print(wine)
###########################
# 程序信息
###########################
# 语言载入
if not "zh_CN".lower() in get_now_lang().lower():
transla = Trans("en_US", f"{programPath}/trans/deepin-wine-runner.json")
else:
transla = Trans("zh_CN")
iconPath = "{}/deepin-wine-runner.svg".format(programPath)
#iconPath = "{}/Icon/Program/wine运行器.png".format(programPath)
programUrl = "https://gitee.com/gfdgd-xi/deep-wine-runner\nhttps://github.com/gfdgd-xi/deep-wine-runner\nhttps://www.gitlink.org.cn/gfdgd_xi/deep-wine-runner\nhttps://gfdgd-xi.github.io"
information = json.loads(readtxt(f"{programPath}/information.json"))
version = information["Version"]
goodRunSystem = transla.transe("U", "常见 Linux 发行版")
thankText = ""
tips = transla.transe("U", '''
N: 鉴于仓库 'https://community-packages.deepin.com/beige beige InRelease' 不支持 'i386' 体系结构,跳过配置文件 'main/binary-i386/Packages' 的获取。''')
updateThingsString = transla.transe("U", '''※1、不基于生态适配脚本打包器支持禁用 Mono/Gecko 打包器;
※2、自动容器配置脚本新增命令 decompressionbottle、programforum、installmsi 以及上述命令的帮助;
※3、自动容器配置脚本新增评论功能;
※4、自动容器配置脚本新增许多应用安装脚本;
※5、安装 Windows 虚拟机功能更换应答镜像图标并添加常用 Windows 组件安装功能;
※6、安装 Windows 虚拟机功能提供镜像下载的网盘链接;
※7、简易打包器支持自动添加宋体;
8、修复 installfont 命令下载的字体目录错误问题;
9、修复提交日志功能在提交成功时依旧提示提交失败问题;
10、支持强制启用所有被禁用的组件(不推荐)。
''')
for i in information["Thank"]:
thankText += f"{i}\n"
updateTime = "2023年01月06日"
aboutProgram = transla.transe("U", """Wine运行器是一个能让Linux用户更加方便地运行Windows应用的程序,内置了对Wine图形化的支持、各种Wine工具、自制的Wine程序打包器和运行库安装工具等。
它同时还内置了基于VirtualBox制作的、专供小白使用的Windows虚拟机安装工具,可以做到只需下载系统镜像并点击安装即可,无需考虑虚拟机的安装、创建、分区等操作。
一个图形化了如下命令的程序(最简单格式)env WINEPREFIX=容器路径 wine(wine的路径) 可执行文件路径让你可以简易方便的使用 wine""") about = f'''关于
{aboutProgram} 版本:{version} 适用平台:{goodRunSystem}(@VersionForType@) Qt 版本:{QtCore.qVersion()} 程序官网:{programUrl} 当前程序占用体积:@programSize@MB
本程序依照 GPLV3 协议开源
{thankText}
{updateThingsString}
更新时间:{updateTime}
{tips}
是由 @gfdgd xi 带头的团队,gfdgd xi开发了UEngine运行器等好用的开源软件。
星火应用商店:https://spark-app.store/ Deepin 官网:https://www.deepin.org Deepin 论坛:https://bbs.deepin.org 论坛:https://gfdgdxi.flarum.cloud/