#!/usr/bin/env python3
# MTK Flash Client (c) B.Kerler, G.Kreileman 2021.
# Licensed under GPLv3 License
import sys
import time
import mock
import threading
import logging
import ctypes
from functools import partial
from PySide6.QtCore import Qt, QVariantAnimation, Signal, QObject, QSize, QTranslator, QLocale, QLibraryInfo, \
Slot, QCoreApplication
from PySide6.QtGui import QTextOption, QPixmap, QTransform, QIcon
from PySide6.QtWidgets import QMainWindow, QApplication, QWidget, QCheckBox, QVBoxLayout, QHBoxLayout, QLineEdit, \
QPushButton
from mtkclient.Library.mtk_class import Mtk
from mtkclient.Library.DA.mtk_da_handler import DA_handler
from mtkclient.Library.gpt import gpt_settings
from mtkclient.Library.mtk_main import Main, Mtk_Config
from mtkclient.gui.readFlashPartitions import ReadFlashWindow
from mtkclient.gui.writeFlashPartitions import WriteFlashWindow
from mtkclient.gui.eraseFlashPartitions import EraseFlashWindow
from mtkclient.gui.toolsMenu import generateKeysMenu, UnlockMenu
from mtkclient.gui.toolkit import asyncThread, trap_exc_during_debug, convert_size, CheckBox, FDialog, TimeEstim
from mtkclient.config.payloads import pathconfig
from mtkclient.gui.main_gui import Ui_MainWindow
import os
lock = threading.Lock()
os.environ['QT_MAC_WANTS_LAYER'] = '1' # This fixes a bug in pyside2 on MacOS Big Sur
# TO do Move all GUI modifications to signals!
# install exception hook: without this, uncaught exception would cause application to exit
sys.excepthook = trap_exc_during_debug
# Initiate MTK classes
variables = mock.Mock()
variables.cmd = "stage"
variables.debugmode = True
path = pathconfig()
# if sys.platform.startswith('darwin'):
# config.ptype = "kamakiri" #Temp for Mac testing
MtkTool = Main(variables)
guiState = "welcome"
phoneInfo = {"chipset": "", "bootMode": "", "daInit": False, "cdcInit": False}
class DeviceHandler(QObject):
sendToLogSignal = Signal(str)
update_status_text = Signal(str)
sendToProgressSignal = Signal(int)
da_handler = None
def __init__(self, parent, preloader: str = None, loglevel=logging.INFO, *args, **kwargs):
super().__init__(parent, *args, **kwargs)
config = Mtk_Config(loglevel=logging.INFO, gui=self.sendToLogSignal, guiprogress=self.sendToProgressSignal,
update_status_text=self.update_status_text)
config.gpt_settings = gpt_settings(gpt_num_part_entries='0', gpt_part_entry_size='0',
gpt_part_entry_start_lba='0') # This actually sets the right GPT settings..
config.reconnect = True
config.uartloglevel = 2
self.loglevel = logging.DEBUG
self.da_handler = DA_handler(Mtk(config=config, loglevel=logging.INFO), loglevel)
def getDevInfo(self, parameters):
# loglevel = parameters[0]
phoneInfo = parameters[1]
devhandler = parameters[2]
mtkClass = devhandler.da_handler.mtk
da_handler = devhandler.da_handler
try:
if not mtkClass.port.cdc.connect():
mtkClass.preloader.init()
else:
phoneInfo['cdcInit'] = True
except Exception:
phoneInfo['cantConnect'] = True
phoneInfo['chipset'] = (str(mtkClass.config.chipconfig.name) + " (" + str(mtkClass.config.chipconfig.description) +
")")
self.sendUpdateSignal.emit()
mtkClass = da_handler.configure_da(mtkClass, preloader=None)
if mtkClass:
phoneInfo['daInit'] = True
phoneInfo['chipset'] = (str(mtkClass.config.chipconfig.name) + " (" +
str(mtkClass.config.chipconfig.description) + ")")
if mtkClass.config.is_brom:
phoneInfo['bootMode'] = "Bootrom mode"
elif mtkClass.config.chipconfig.damode:
phoneInfo['bootMode'] = "DA mode"
else:
phoneInfo['bootMode'] = "Preloader mode"
self.sendUpdateSignal.emit()
else:
phoneInfo['cantConnect'] = True
self.sendUpdateSignal.emit()
def load_translations(application):
# Load application translations and the QT base translations for the current locale
locale = QLocale.system()
translator = QTranslator(application)
directory = os.path.dirname(__file__)
lang = 'mtkclient/gui/i18n/' + locale.name()
if locale.name() == "en_NL":
lang = lang.replace("en_NL", "nl_NL")
# lang = 'mtkclient/gui/i18n/fr_FR'
# lang = 'mtkclient/gui/i18n/de_DE'
# lang = 'mtkclient/gui/i18n/en_GB'
# lang = 'mtkclient/gui/i18n/es_ES'
if translator.load(lang, directory):
application.installTranslator(translator)
translations_path = QLibraryInfo.path(QLibraryInfo.TranslationsPath)
base_translator = QTranslator(application)
if base_translator.load(locale, "qtbase", "_", translations_path):
application.installTranslator(base_translator)
class MainWindow(QMainWindow):
def __init__(self):
super(MainWindow, self).__init__()
self.ui = Ui_MainWindow()
self.ui.setupUi(self)
self.fdialog = FDialog(self)
self.initpixmap()
self.Status = {}
self.timeEst = TimeEstim()
self.timeEstTotal = TimeEstim()
self.ui.logBox.setWordWrapMode(QTextOption.NoWrap)
self.ui.menubar.setEnabled(False)
self.ui.tabWidget.setHidden(True)
self.ui.partProgress.setHidden(True)
self.ui.fullProgress.setHidden(True)
self.ui.readDumpGPTCheckbox.setChecked(True)
self.ui.connectInfo.setMinimumSize(200, 500)
self.ui.connectInfo.setMaximumSize(9900, 500)
self.ui.showdebugbtn.clicked.connect(self.showDebugInfo)
self.devhandler = None
self.readflash = None
def showDebugInfo(self):
self.ui.connectInfo.setHidden(True)
self.ui.tabWidget.setCurrentWidget(self.ui.debugtab)
self.ui.tabWidget.setHidden(False)
@Slot()
def updateState(self):
global lock
lock.acquire()
doneBytes = 0
if "currentPartitionSizeDone" in self.Status:
curpartBytes = self.Status["currentPartitionSizeDone"]
else:
curpartBytes = self.Status["currentPartitionSize"]
if "allPartitions" in self.Status:
for partition in self.Status["allPartitions"]:
if self.Status["allPartitions"][partition]['done'] and partition != self.Status["currentPartition"]:
doneBytes = doneBytes + self.Status["allPartitions"][partition]['size']
doneBytes = curpartBytes + doneBytes
totalBytes = self.Status["totalsize"]
fullPercentageDone = int((doneBytes / totalBytes) * 100)
self.ui.fullProgress.setValue(fullPercentageDone)
timeinfototal = self.timeEstTotal.update(fullPercentageDone, 100)
self.ui.fullProgressText.setText("
Total: " +
convert_size(doneBytes) + " / " + convert_size(totalBytes) +
" | " + timeinfototal +
QCoreApplication.translate("main", " left") +
" |
")
else:
partBytes = self.Status["currentPartitionSize"]
doneBytes = self.Status["currentPartitionSizeDone"]
fullPercentageDone = int((doneBytes / partBytes) * 100)
self.ui.fullProgress.setValue(fullPercentageDone)
timeinfototal = self.timeEstTotal.update(fullPercentageDone, 100)
self.ui.fullProgressText.setText("Total: " +
convert_size(doneBytes) + " / " + convert_size(partBytes) +
" | " +
timeinfototal + QCoreApplication.translate("main",
" left") + " |
")
if "currentPartitionSize" in self.Status:
partBytes = self.Status["currentPartitionSize"]
partDone = (curpartBytes / partBytes) * 100
self.ui.partProgress.setValue(partDone)
timeinfo = self.timeEst.update(curpartBytes, partBytes)
txt = "Current partition: " + self.Status["currentPartition"] + \
" (" + convert_size(curpartBytes) + " / " + convert_size(partBytes) + ") | " + \
timeinfo + QCoreApplication.translate("main", " left") + " |
"
self.ui.partProgressText.setText(txt)
lock.release()
def updateStateAsync(self, toolkit, parameters):
while not self.Status["done"]:
# print(self.dumpStatus)
time.sleep(0.1)
print("DONE")
self.ui.readpreloaderbtn.setEnabled(True)
self.ui.readpartitionsbtn.setEnabled(True)
self.ui.readboot2btn.setEnabled(True)
self.ui.readrpmbbtn.setEnabled(True)
self.ui.readflashbtn.setEnabled(True)
self.ui.writepartbtn.setEnabled(True)
self.ui.writeflashbtn.setEnabled(True)
self.ui.writeboot2btn.setEnabled(True)
self.ui.writepreloaderbtn.setEnabled(True)
self.ui.writerpmbbtn.setEnabled(True)
self.ui.erasepartitionsbtn.setEnabled(True)
self.ui.eraseboot2btn.setEnabled(True)
self.ui.erasepreloaderbtn.setEnabled(True)
self.ui.eraserpmbbtn.setEnabled(True)
@Slot(int)
def updateProgress(self, progress):
try:
if self.Status["rpmb"]:
self.Status["currentPartitionSizeDone"] = progress
else:
self.Status[
"currentPartitionSizeDone"] = progress * self.devhandler.da_handler.mtk.daloader.progress.pagesize
self.updateState()
except Exception:
pass
def setdevhandler(self, devhandler):
self.devhandler = devhandler
devhandler.sendToProgressSignal.connect(self.updateProgress)
devhandler.update_status_text.connect(self.update_status_text)
def initread(self):
self.readflash = ReadFlashWindow(self.ui, self, self.devhandler.da_handler, self.sendToLog)
thread.sendUpdateSignal.connect(win.updateGui)
self.readflash.enableButtonsSignal.connect(self.enablebuttons)
self.readflash.disableButtonsSignal.connect(self.disablebuttons)
self.ui.readpartitionsbtn.clicked.connect(self.readflash.dumpPartition)
self.ui.readselectallcheckbox.clicked.connect(self.readflash.selectAll)
self.ui.readpreloaderbtn.clicked.connect(self.on_readpreloader)
self.ui.readflashbtn.clicked.connect(self.on_readfullflash)
self.ui.readrpmbbtn.clicked.connect(self.on_readrpmb)
self.ui.readboot2btn.clicked.connect(self.on_readboot2)
def initkeys(self):
self.genkeys = generateKeysMenu(self.ui, self, self.devhandler.da_handler, self.sendToLog)
self.ui.generatekeybtn.clicked.connect(self.on_generatekeys)
self.genkeys.enableButtonsSignal.connect(self.enablebuttons)
self.genkeys.disableButtonsSignal.connect(self.disablebuttons)
def initunlock(self):
self.unlock = UnlockMenu(self.ui, self, self.devhandler.da_handler, self.sendToLog)
self.ui.unlockbutton.clicked.connect(self.on_unlock)
self.ui.lockbutton.clicked.connect(self.on_lock)
self.unlock.enableButtonsSignal.connect(self.enablebuttons)
self.unlock.disableButtonsSignal.connect(self.disablebuttons)
def initerase(self):
self.eraseflash = EraseFlashWindow(self.ui, self, self.devhandler.da_handler, self.sendToLog)
self.eraseflash.enableButtonsSignal.connect(self.enablebuttons)
self.eraseflash.disableButtonsSignal.connect(self.disablebuttons)
self.ui.eraseselectallpartitionscheckbox.clicked.connect(self.eraseflash.selectAll)
self.ui.erasepartitionsbtn.clicked.connect(self.on_erasepartflash)
self.ui.eraserpmbbtn.clicked.connect(self.on_eraserpmb)
self.ui.erasepreloaderbtn.clicked.connect(self.on_erasepreloader)
self.ui.eraseboot2btn.clicked.connect(self.on_eraseboot2)
def initwrite(self):
self.writeflash = WriteFlashWindow(self.ui, self, self.devhandler.da_handler, self.sendToLog)
self.writeflash.enableButtonsSignal.connect(self.enablebuttons)
self.writeflash.disableButtonsSignal.connect(self.disablebuttons)
self.ui.writeselectfromdir.clicked.connect(self.writeflash.selectFiles)
self.ui.writeflashbtn.clicked.connect(self.on_writefullflash)
self.ui.writepartbtn.clicked.connect(self.on_writepartflash)
self.ui.writeboot2btn.clicked.connect(self.on_writeboot2)
self.ui.writepreloaderbtn.clicked.connect(self.on_writepreloader)
self.ui.writerpmbbtn.clicked.connect(self.on_writerpmb)
@Slot(str)
def update_status_text(self, text):
self.ui.phoneDebugInfoTextbox.setText(text)
@Slot()
def disablebuttons(self):
self.ui.readpreloaderbtn.setEnabled(False)
self.ui.readpartitionsbtn.setEnabled(False)
self.ui.readboot2btn.setEnabled(False)
self.ui.readrpmbbtn.setEnabled(False)
self.ui.readflashbtn.setEnabled(False)
self.ui.writeflashbtn.setEnabled(False)
self.ui.writepartbtn.setEnabled(False)
self.ui.writepreloaderbtn.setEnabled(False)
self.ui.writeboot2btn.setEnabled(False)
self.ui.writerpmbbtn.setEnabled(False)
self.ui.eraseboot2btn.setEnabled(False)
self.ui.erasepreloaderbtn.setEnabled(False)
self.ui.eraserpmbbtn.setEnabled(False)
self.ui.generatekeybtn.setEnabled(False)
self.ui.unlockbutton.setEnabled(False)
self.ui.lockbutton.setEnabled(False)
@Slot()
def enablebuttons(self):
self.ui.readpreloaderbtn.setEnabled(True)
self.ui.readpartitionsbtn.setEnabled(True)
self.ui.readboot2btn.setEnabled(True)
self.ui.readrpmbbtn.setEnabled(True)
self.ui.readflashbtn.setEnabled(True)
self.ui.writeflashbtn.setEnabled(True)
self.ui.writepartbtn.setEnabled(True)
self.ui.writepreloaderbtn.setEnabled(True)
self.ui.writeboot2btn.setEnabled(True)
self.ui.writerpmbbtn.setEnabled(True)
self.ui.eraseboot2btn.setEnabled(True)
self.ui.erasepreloaderbtn.setEnabled(True)
self.ui.eraserpmbbtn.setEnabled(True)
self.ui.generatekeybtn.setEnabled(True)
self.ui.unlockbutton.setEnabled(True)
self.ui.lockbutton.setEnabled(True)
self.ui.partProgress.setValue(100)
self.ui.fullProgress.setValue(100)
self.ui.fullProgressText.setText("")
self.ui.partProgressText.setText(self.tr("Done."))
self.Status = {}
def getpartitions(self):
data, guid_gpt = self.devhandler.da_handler.mtk.daloader.get_gpt()
if guid_gpt is None:
print("Error reading gpt")
self.ui.readtitle.setText(QCoreApplication.translate("main", "Error reading gpt"))
else:
self.ui.readtitle.setText(QCoreApplication.translate("main", "Select partitions to dump"))
readpartitionListWidgetVBox = QVBoxLayout()
readpartitionListWidget = QWidget(self)
readpartitionListWidget.setLayout(readpartitionListWidgetVBox)
self.ui.readpartitionList.setWidget(readpartitionListWidget)
self.ui.readpartitionList.setWidgetResizable(True)
# self.ui.readpartitionList.setGeometry(10,40,380,320)
self.ui.readpartitionList.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOn)
self.ui.readpartitionList.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
self.readpartitionCheckboxes = {}
for partition in guid_gpt.partentries:
self.readpartitionCheckboxes[partition.name] = {}
self.readpartitionCheckboxes[partition.name]['size'] = (partition.sectors * guid_gpt.sectorsize)
self.readpartitionCheckboxes[partition.name]['box'] = QCheckBox()
self.readpartitionCheckboxes[partition.name]['box'].setText(partition.name + " (" +
convert_size(
partition.sectors * guid_gpt.sectorsize) + ")")
readpartitionListWidgetVBox.addWidget(self.readpartitionCheckboxes[partition.name]['box'])
writepartitionListWidgetVBox = QVBoxLayout()
writepartitionListWidget = QWidget(self)
writepartitionListWidget.setLayout(writepartitionListWidgetVBox)
self.ui.writepartitionList.setWidget(writepartitionListWidget)
self.ui.writepartitionList.setWidgetResizable(True)
# self.ui.writepartitionList.setGeometry(10,40,380,320)
self.ui.writepartitionList.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOn)
self.ui.writepartitionList.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
self.writepartitionCheckboxes = {}
for partition in guid_gpt.partentries:
self.writepartitionCheckboxes[partition.name] = {}
self.writepartitionCheckboxes[partition.name]['size'] = (partition.sectors * guid_gpt.sectorsize)
vb = QVBoxLayout()
qc = CheckBox()
qc.setReadOnly(True)
qc.setText(partition.name + " (" + convert_size(partition.sectors * guid_gpt.sectorsize) + ")")
hc = QHBoxLayout()
ll = QLineEdit()
lb = QPushButton(QCoreApplication.translate("main", "Set"))
lb.clicked.connect(partial(self.selectWriteFile, partition.name, qc, ll))
hc.addWidget(ll)
hc.addWidget(lb)
vb.addWidget(qc)
vb.addLayout(hc)
ll.setDisabled(True)
self.writepartitionCheckboxes[partition.name]['box'] = [qc, ll, lb]
writepartitionListWidgetVBox.addLayout(vb)
erasepartitionListWidgetVBox = QVBoxLayout()
erasepartitionListWidget = QWidget(self)
erasepartitionListWidget.setLayout(erasepartitionListWidgetVBox)
self.ui.erasepartitionList.setWidget(erasepartitionListWidget)
self.ui.erasepartitionList.setWidgetResizable(True)
# self.ui.erasepartitionList.setGeometry(10,40,380,320)
self.ui.erasepartitionList.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOn)
self.ui.erasepartitionList.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
self.erasepartitionCheckboxes = {}
for partition in guid_gpt.partentries:
self.erasepartitionCheckboxes[partition.name] = {}
self.erasepartitionCheckboxes[partition.name]['size'] = (partition.sectors * guid_gpt.sectorsize)
self.erasepartitionCheckboxes[partition.name]['box'] = QCheckBox()
self.erasepartitionCheckboxes[partition.name]['box'].setText(partition.name + " (" +
convert_size(
partition.sectors * guid_gpt.sectorsize) + ")")
erasepartitionListWidgetVBox.addWidget(self.erasepartitionCheckboxes[partition.name]['box'])
def selectWriteFile(self, partition, checkbox, lineedit):
fname = self.fdialog.open(partition + ".bin")
if fname is None:
checkbox.setChecked(False)
lineedit.setText("")
lineedit.setDisabled(True)
return ""
checkbox.setChecked(True)
lineedit.setText(fname)
lineedit.setDisabled(False)
return fname
def on_writefullflash(self):
self.writeflash.writeFlash("user")
return
def on_writepreloader(self):
self.writeflash.writeFlash("boot1")
return
def on_writeboot2(self):
self.writeflash.writeFlash("boot2")
return
def on_writerpmb(self):
self.writeflash.writeFlash("rpmb")
return
def on_writepartflash(self):
self.writeflash.writePartition()
return
def on_erasepartflash(self):
self.eraseflash.erasePartition()
return
def on_eraseboot2(self):
self.eraseflash.eraseBoot2()
def on_erasepreloader(self):
self.eraseflash.erasePreloader()
def on_eraserpmb(self):
self.eraseflash.eraseRpmb()
def on_generatekeys(self):
self.genkeys.generateKeys()
def on_unlock(self):
self.unlock.unlock("unlock")
def on_lock(self):
self.unlock.unlock("lock")
def on_readpreloader(self):
self.readflash.dumpFlash("boot1")
def on_readboot2(self):
self.readflash.dumpFlash("boot2")
return
def on_readfullflash(self):
self.readflash.dumpFlash("user")
def on_readrpmb(self):
self.readflash.dumpFlash("rpmb")
return
def sendToLog(self, info):
t = time.localtime()
self.ui.logBox.appendPlainText(time.strftime("[%H:%M:%S", t) + "]: " + info)
self.ui.logBox.verticalScrollBar().setValue(self.ui.logBox.verticalScrollBar().maximum())
def sendToProgress(self, progress):
return
def updateGui(self):
global phoneInfo
phoneInfo['chipset'] = phoneInfo['chipset'].replace("()", "")
if phoneInfo['cdcInit'] and phoneInfo['bootMode'] == "":
self.ui.phoneInfoTextbox.setText(
QCoreApplication.translate("main", "Phone detected:\nReading model info..."))
else:
self.ui.phoneInfoTextbox.setText(QCoreApplication.translate("main",
"Phone detected:\n" + phoneInfo[
'chipset'] + "\n" + phoneInfo['bootMode']))
# Disabled due to graphical steps. Maybe this should come back somewhere else.
# self.ui.status.setText(QCoreApplication.translate("main","Device detected, please wait.\nThis can take a while..."))
if phoneInfo['daInit']:
# self.ui.status.setText(QCoreApplication.translate("main","Device connected :)"))
self.ui.menubar.setEnabled(True)
self.pixmap = QPixmap(path.get_images_path("phone_connected.png"))
self.ui.phoneDebugInfoTextbox.setText("")
self.ui.pic.setPixmap(self.pixmap)
self.spinnerAnim.stop()
self.ui.spinner_pic.setHidden(True)
self.ui.connectInfo.setHidden(True)
self.ui.partProgress.setHidden(False)
self.ui.fullProgress.setHidden(False)
self.initread()
self.initkeys()
self.initunlock()
self.initerase()
self.initwrite()
self.getpartitions()
self.ui.tabWidget.setCurrentIndex(0)
self.ui.tabWidget.update()
self.ui.tabWidget.setHidden(False)
else:
if 'cantConnect' in phoneInfo:
self.ui.status.setText(
QCoreApplication.translate("main", "Error initialising. Did you install the drivers?"))
self.spinnerAnim.start()
self.ui.spinner_pic.setHidden(False)
def spinnerAnimRot(self, angle):
# trans = QTransform()
# dimension = self.pixmap.width() / math.sqrt(2)
newPixmap = self.pixmap.transformed(QTransform().rotate(angle), Qt.SmoothTransformation)
xoffset = (newPixmap.width() - self.pixmap.width()) / 2
yoffset = (newPixmap.height() - self.pixmap.height()) / 2
rotated = newPixmap.copy(xoffset, yoffset, self.pixmap.width(), self.pixmap.height())
self.ui.spinner_pic.setPixmap(rotated)
def initpixmap(self):
# phone spinner
self.pixmap = QPixmap(path.get_images_path("phone_loading.png")).scaled(96, 96, Qt.KeepAspectRatio,
Qt.SmoothTransformation)
self.pixmap.setDevicePixelRatio(2)
self.ui.spinner_pic.setPixmap(self.pixmap)
self.ui.spinner_pic.show()
nfpixmap = QPixmap(path.get_images_path("phone_notfound.png"))
self.ui.pic.setPixmap(nfpixmap)
logo = QPixmap(path.get_images_path("logo_256.png"))
self.ui.logoPic.setPixmap(logo)
initSteps = QPixmap(path.get_images_path("initsteps.png"))
self.ui.initStepsImage.setPixmap(initSteps)
self.spinnerAnim = QVariantAnimation()
self.spinnerAnim.setDuration(3000)
self.spinnerAnim.setStartValue(0)
self.spinnerAnim.setEndValue(360)
self.spinnerAnim.setLoopCount(-1)
self.spinnerAnim.valueChanged.connect(self.spinnerAnimRot)
self.ui.spinner_pic.setHidden(True)
if __name__ == '__main__':
# Enable nice 4K Scaling
os.environ["QT_AUTO_SCREEN_SCALE_FACTOR"] = "1"
# Init the app window
app = QApplication(sys.argv)
load_translations(app)
win = MainWindow()
icon = QIcon()
icon.addFile(path.get_images_path('logo_32.png'), QSize(32, 32))
icon.addFile(path.get_images_path('logo_64.png'), QSize(64, 64))
icon.addFile(path.get_images_path('logo_256.png'), QSize(256, 256))
icon.addFile(path.get_images_path('logo_512.png'), QSize(512, 512))
app.setWindowIcon(icon)
win.setWindowIcon(icon)
if sys.platform.startswith('win'):
ctypes.windll.shell32.SetCurrentProcessExplicitAppUserModelID('MTKTools.Gui')
dpiMultiplier = win.logicalDpiX()
if dpiMultiplier == 72:
dpiMultiplier = 2
else:
dpiMultiplier = 1
addTopMargin = 20
if sys.platform.startswith('darwin'): # MacOS has the toolbar in the top bar insted of in the app...
addTopMargin = 0
win.setWindowTitle("MTKClient - Version 2.0")
# lay = QVBoxLayout(self)
win.show()
# win.setFixedSize(746, 400 + addTopMargin)
# Device setup
loglevel = logging.INFO
devhandler = DeviceHandler(parent=app, preloader=None, loglevel=loglevel)
devhandler.sendToLogSignal.connect(win.sendToLog)
# Get the device info
thread = asyncThread(parent=app, n=0, function=getDevInfo, parameters=[loglevel, phoneInfo, devhandler])
thread.sendToLogSignal.connect(win.sendToLog)
thread.sendUpdateSignal.connect(win.updateGui)
thread.sendToProgressSignal.connect(win.sendToProgress)
thread.start()
win.setdevhandler(devhandler)
# Run loop the app
app.exec()
# Prevent thread from not being closed and call error end codes
thread.terminate()
thread.wait()