实例介绍
【实例简介】
本项目是一个跨平台开源串口调试助手,使用 python 编写
【实例截图】
【核心代码】
import sys,os
if sys.version_info < (3, 7):
print("only support python >= 3.7, but now is {}".format(sys.version_info))
sys.exit(1)
# Init lanuage first to ensure use function _ works correctly
try:
import parameters
import i18n
except ImportError:
from COMTool import parameters
from COMTool import i18n
def loadConfig():
paramObj = parameters.Parameters()
paramObj.load(parameters.configFilePath)
return paramObj
log = parameters.log
log.i("loading config from", parameters.configFilePath)
programConfig = loadConfig()
log.i("loading config complete")
i18n.set_locale(programConfig["locale"])
try:
import helpAbout,autoUpdate
from Combobox import ComboBox
from i18n import _
import version
import utils_ui
from conn import ConnectionStatus, conns
from plugins import builtinPlugins
from pluginItems import PluginItem
from widgets import TitleBar, CustomTitleBarWindowMixin, EventFilter, ButtonCombbox, HelpWidget
except ImportError:
from COMTool import helpAbout,autoUpdate, utils_ui
from COMTool.Combobox import ComboBox
from COMTool.i18n import _
from COMTool import version
from COMTool.conn import ConnectionStatus, conns
from COMTool.plugins import builtinPlugins
from COMTool.pluginItems import PluginItem
from .widgets import TitleBar, CustomTitleBarWindowMixin, EventFilter, ButtonCombbox, HelpWidget
from PyQt5.QtCore import pyqtSignal, Qt, QRect, QMargins, QCoreApplication
from PyQt5.QtWidgets import (QApplication, QWidget,QPushButton,QMessageBox,QDesktopWidget,QMainWindow,
QVBoxLayout,QHBoxLayout,QGridLayout,QTextEdit,QLabel,QRadioButton,QCheckBox,
QLineEdit,QGroupBox,QSplitter,QFileDialog, QScrollArea, QTabWidget, QMenu, QSplashScreen)
from PyQt5.QtGui import QIcon,QFont,QTextCursor,QPixmap,QColor, QCloseEvent
import qtawesome as qta # https://github.com/spyder-ide/qtawesome
import threading
import time
from datetime import datetime
import binascii,re
if sys.platform == "win32":
import ctypes
g_all_windows = []
class MainWindow(CustomTitleBarWindowMixin, QMainWindow):
hintSignal = pyqtSignal(str, str, str) # type(error, warning, info), title, msg
# statusBarSignal = pyqtSignal(str, str)
updateSignal = pyqtSignal(object)
# countUpdateSignal = pyqtSignal(int, int)
reloadWindowSignal = pyqtSignal(str, str, object)
receiveCount = 0
sendCount = 0
DataPath = "./"
app = None
needRestart = False
def __init__(self,app, eventFilter, config):
QMainWindow.__init__(self)
self.app = app
self.eventFilter = eventFilter
self.DataPath = parameters.dataPath
self.config = config
log.i("init main window")
self.initVar()
self.initWindow()
self.uiLoadConfigs()
log.i("init main window complete")
self.loadPluginsInfoList()
self.loadPluginItems()
log.i("load plugin items complete")
self.initEvent()
def initVar(self):
self.loadPluginStr = _("Load plugin from file")
self.closeTimerId = None
self.items = []
self.pluginClasses = []
self.helpWindow = None
def loadPluginsInfoList(self):
for id, pluginClass in builtinPlugins.items():
self.addPluginInfo(pluginClass)
self.pluginClasses.append(pluginClass)
rm = []
for uid, info in self.config["pluginsInfo"]["external"].items():
pluginClass = self._importPlugin(info["path"], test=True)
if pluginClass:
self.addPluginInfo(pluginClass)
self.pluginClasses.append(pluginClass)
else:
rm.append(uid)
for uid in rm:
self.config["pluginsInfo"]["external"].pop(uid)
# find in python packages
ignore_paths = ["DLLs"]
for path in sys.path:
if os.path.isdir(path) and not path in ignore_paths:
for name in os.listdir(path):
if name.lower().startswith("comtool_plugin_") and not name.endswith("dist-info"):
log.i(f"find plugin package <{name}>")
pluginClass = __import__(name).Plugin
print(pluginClass, self.pluginClasses)
if not pluginClass in self.pluginClasses:
self.addPluginInfo(pluginClass)
self.pluginClasses.append(pluginClass)
def getPluginClassById(self, id):
'''
must call after loadPluginsInfoList
'''
for pluginClass in self.pluginClasses:
if id == pluginClass.id:
return pluginClass
return None
def loadPluginItems(self):
items = self.config["items"]
if items:
for item in items:
log.i("load plugin item", item["name"])
pluginClass = self.getPluginClassById(item["pluginId"])
if pluginClass:
setCurr = False
if self.config["currItem"] == item["name"]:
setCurr = True
# check language change, update item name to current lanuage
old_name_tail = item["name"].split(" ")[-1]
try:
int(old_name_tail)
item["name"] = pluginClass.name " " old_name_tail
except Exception: # for no number tailed name
item["name"] = pluginClass.name
self.addItem(pluginClass, nameSaved=item["name"], setCurrent=setCurr, connsConfigs = item["config"]["conns"], pluginConfig=item["config"]["plugin"])
else: # load builtin plugins
for id, pluginClass in builtinPlugins.items():
self.addItem(pluginClass)
def addItem(self, pluginClass, nameSaved = None, setCurrent = False, connsConfigs=None, pluginConfig=None):
'''
@name add saved item, not add new item
'''
# set here, not set in arg, cause arg can lead to multi item use one object
if not connsConfigs:
connsConfigs = {}
if not pluginConfig:
pluginConfig = {}
if nameSaved:
name = nameSaved
else:
numbers = []
for item in self.items:
if item.plugin.id == pluginClass.id:
name = item.name.replace(item.plugin.name, "").split(" ")
if len(name) > 1:
number = int(name[-1])
numbers.append(number)
else:
numbers.append(0)
if numbers:
numbers = sorted(numbers)
if (not numbers) or numbers[0] != 0:
name = pluginClass.name
else:
last = numbers[0]
number = -1
for n in numbers[1:]:
if n != last 1:
number = last 1
break
last = n
if number < 0:
number = numbers[-1] 1
name = f'{pluginClass.name} {number}'
item = PluginItem(name, pluginClass,
conns, connsConfigs,
self.config, pluginConfig,
self.hintSignal, self.reloadWindowSignal,
self.onConnChnaged)
self.tabAddItem(item)
self.items.append(item)
if setCurrent:
self.tabWidget.setCurrentWidget(item.widget)
if not nameSaved:
self.config["items"].append({
"name": name,
"pluginId": pluginClass.id,
"config": {
"conns": connsConfigs,
"plugin": pluginConfig
}
})
return item
def tabAddItem(self, item):
self.tabWidget.addTab(item.widget, item.name)
self.tabWidget.setTabToolTip(self.tabWidget.count() - 1, item.name _(", Double click to detach as a window"))
def onConnChnaged(self, plugin, status:ConnectionStatus, msg):
for item in self.items:
if item.plugin == plugin:
for i in range(self.tabWidget.count()):
if self.tabWidget.widget(i) == item.widget:
self.setTabIcon(status, i)
break
item.widget.setWindowTitle(item.name " - {}".format(_("Connected" if status == ConnectionStatus.CONNECTED else _("Connection lose") if status == ConnectionStatus.LOSE else _("Disconnected"))))
def setTabIcon(self, status:ConnectionStatus, i:int):
if status == ConnectionStatus.CONNECTED:
self.tabWidget.setTabIcon(i, qta.icon("fa.circle", color="#4caf50"))
elif status == ConnectionStatus.LOSE:
self.tabWidget.setTabIcon(i, qta.icon("fa.circle", color="orange"))
else:
self.tabWidget.setTabIcon(i, QIcon())
def addPluginInfo(self, pluginClass):
self.pluginsSelector.insertItem(self.pluginsSelector.count() - 1,
f'{pluginClass.name} - {pluginClass.id}')
def _importPlugin(self, path, test = False):
if not os.path.exists(path):
return None
dir = os.path.dirname(path)
name = os.path.splitext(os.path.basename(path))[0]
sys.path.insert(0, dir)
try:
print("import")
pluginClass = __import__(name).Plugin
except Exception as e:
import traceback
msg = traceback.format_exc()
self.hintSignal.emit("error", _("Error"), '{}: {}'.format(_("Load plugin failed"), msg))
return None
if test:
sys.path.remove(dir)
return pluginClass
def loadExternalPlugin(self, path):
extPlugsInfo = self.config["pluginsInfo"]["external"]
found = False
for uid, info in extPlugsInfo.items():
if info["path"] == path:
for pluginClass in self.pluginClasses:
# same plugin
if uid == pluginClass.id:
self.addItem(pluginClass, setCurrent = True)
found = True
break
if found:
return True, ""
pluginClass = self._importPlugin(path)
if not pluginClass:
return False, _("Load plugin fail")
extPlugsInfo[pluginClass.id] = {
"path": path
}
if not pluginClass in self.pluginClasses:
self.pluginClasses.append(pluginClass)
self.addPluginInfo(pluginClass)
# find old item config for this plugin and recover
oldFound = False
for item in self.config["items"]:
if item["pluginId"] == pluginClass.id:
self.addItem(pluginClass, nameSaved=item["name"], setCurrent=True, connsConfigs=item["config"]["conns"], pluginConfig=item["config"]["plugin"])
oldFound = True
break
if not oldFound:
self.addItem(pluginClass, setCurrent=True)
return True, ""
def onPluginSelectorChanged(self, idx):
text = self.pluginsSelector.currentText()
if text == self.loadPluginStr:
oldPath = os.getcwd()
fileName_choose, filetype = QFileDialog.getOpenFileName(self,
_("Select file"),
oldPath,
_("python script (*.py)"))
if fileName_choose != "":
ok, msg = self.loadExternalPlugin(fileName_choose)
if not ok:
self.hintSignal.emit("error", _("Error"), f'{_("Load plugin error")}: {msg}')
else:
loadID = text.split("-")[-1].strip()
for pluginClass in self.pluginClasses:
if loadID == pluginClass.id:
self.addItem(pluginClass, setCurrent = True)
break
def initWindow(self):
# set skin for utils_ui
utils_ui.setSkin(self.config["skin"])
# menu layout
self.settingsButton = QPushButton()
self.skinButton = ButtonCombbox(icon=None, btnClass="menuItem", btnId="menuItem2")
self.languageCombobox = ButtonCombbox(icon=None, btnClass="menuItem", btnId="menuItemLang")
self.languages = i18n.get_languages()
for locale in self.languages:
self.languageCombobox.addItem(self.languages[locale])
for skin_name in utils_ui.get_skins():
self.skinButton.addItem(_(skin_name))
self.aboutButton = QPushButton()
self.functionalButton = QPushButton()
self.encodingCombobox = ComboBox()
self.supportedEncoding = parameters.encodings
for encoding in self.supportedEncoding:
self.encodingCombobox.addItem(encoding)
self.settingsButton.setProperty("class", "menuItem")
self.aboutButton.setProperty("class", "menuItem")
self.functionalButton.setProperty("class", "menuItem")
self.settingsButton.setObjectName("menuItem1")
self.aboutButton.setObjectName("menuItem3")
self.functionalButton.setObjectName("menuItem4")
# plugins slector
self.pluginsSelector = ButtonCombbox(icon="fa.plus", btnClass="smallBtn2")
self.pluginsSelector.addItem(self.loadPluginStr)
self.pluginsSelector.activated.connect(self.onPluginSelectorChanged)
# title bar
title = parameters.appName " v" version.__version__
iconPath = self.DataPath "/" parameters.appIcon
log.i("icon path: " iconPath)
self.titleBar = TitleBar(self, icon=iconPath, title=title, brothers=[], widgets=[[self.skinButton, self.languageCombobox, self.aboutButton], []])
CustomTitleBarWindowMixin.__init__(self, titleBar=self.titleBar, init = True)
# root layout
self.frameWidget = QWidget()
self.frameWidget.setMouseTracking(True)
self.frameWidget.setLayout(self.rootLayout)
self.setCentralWidget(self.frameWidget)
# tab widgets
self.tabWidget = QTabWidget()
self.tabWidget.setTabsClosable(True)
# tab left menu
tabConerWidget = QWidget()
tabConerLayout = QHBoxLayout()
tabConerLayout.setSpacing(0)
tabConerLayout.setContentsMargins(0, 0, 0, 0)
tabConerWidget.setLayout(tabConerLayout)
tabConerLayout.addWidget(self.settingsButton)
# tab right menu
tabConerWidgetRight = QWidget()
tabConerLayoutRight = QHBoxLayout()
tabConerLayoutRight.setSpacing(0)
tabConerLayoutRight.setContentsMargins(0, 0, 0, 0)
tabConerWidgetRight.setLayout(tabConerLayoutRight)
tabConerLayoutRight.addWidget(self.pluginsSelector)
tabConerLayoutRight.addWidget(self.encodingCombobox)
tabConerLayoutRight.addWidget(self.functionalButton)
self.tabWidget.setCornerWidget(tabConerWidget, Qt.TopLeftCorner)
self.tabWidget.setCornerWidget(tabConerWidgetRight, Qt.TopRightCorner)
self.contentLayout = QVBoxLayout()
self.contentWidget.setLayout(self.contentLayout)
self.contentLayout.setContentsMargins(10, 0, 10, 10)
self.contentLayout.addWidget(self.tabWidget)
if sys.platform == 'darwin':
self.macOsAddDockMenu()
self.resize(850, 500)
self.MoveToCenter()
self.show()
def add_new_window(self):
import copy
mainWindow = MainWindow(self.app, self.eventFilter, copy.deepcopy(self.config))
self.eventFilter.listenWindow(mainWindow)
g_all_windows.append(mainWindow)
def macOsAddDockMenu(self):
self.dockMenu = QMenu(self)
self.dockMenu.addAction(_('New Window'),
self.add_new_window)
self.dockMenu.setAsDockMenu()
self.app.setAttribute(Qt.AA_DontShowIconsInMenus, True)
def initEvent(self):
# menu
self.settingsButton.clicked.connect(self.toggleSettings)
self.languageCombobox.currentIndexChanged.connect(self.onLanguageChanged)
self.encodingCombobox.currentIndexChanged.connect(lambda: self.bindVar(self.encodingCombobox, self.config, "encoding"))
self.functionalButton.clicked.connect(self.toggleFunctional)
self.skinButton.currentIndexChanged.connect(self.skinChange)
self.aboutButton.clicked.connect(self.showAbout)
# main
self.tabWidget.currentChanged.connect(self.onSwitchTab)
self.tabWidget.tabCloseRequested.connect(self.closeTab)
self.tabWidget.tabBarDoubleClicked.connect(self.onTabDoubleClicked)
# others
self.updateSignal.connect(self.showUpdate)
self.hintSignal.connect(self.showHint)
self.reloadWindowSignal.connect(self.onReloadWindow)
def bindVar(self, uiObj, varObj, varName: str, vtype=None, vErrorMsg="", checkVar=lambda v:v, invert = False):
objType = type(uiObj)
if objType == QCheckBox:
v = uiObj.isChecked()
if hasattr(varObj, varName):
varObj.__setattr__(varName, v if not invert else not v)
else:
varObj[varName] = v if not invert else not v
return
elif objType == QLineEdit:
v = uiObj.text()
elif objType == ComboBox:
if hasattr(varObj, varName):
varObj.__setattr__(varName, uiObj.currentText())
else:
varObj[varName] = uiObj.currentText()
return
elif objType == QRadioButton:
v = uiObj.isChecked()
if hasattr(varObj, varName):
varObj.__setattr__(varName, v if not invert else not v)
else:
varObj[varName] = v if not invert else not v
return
else:
raise Exception("not support this object")
if vtype:
try:
v = vtype(v)
except Exception:
uiObj.setText(str(varObj.__getattribute__(varName)))
self.hintSignal.emit("error", _("Error"), vErrorMsg)
return
try:
v = checkVar(v)
except Exception as e:
self.hintSignal.emit("error", _("Error"), str(e))
return
varObj.__setattr__(varName, v)
def onSwitchTab(self, idx):
item = self.getCurrentItem()
if item:
self.config["currItem"] = item.name
item.plugin.onActive()
def closeTab(self, idx):
# only one, ignore
if self.tabWidget.count() == 1:
return
item = None
for _item in self.items:
if _item.widget == self.tabWidget.widget(idx):
item = _item
break
self.tabWidget.removeTab(idx)
for _item in self.config["items"]:
if _item["name"] == item.name:
self.config["items"].remove(_item)
break
item.onDel()
self.items.remove(item)
def onTabDoubleClicked(self, idx):
item = self.getCurrentItem()
self.tabWidget.removeTab(idx)
parent = item.widget.parent()
item.widget.setWindowFlag(Qt.Window) # this method is not stable in QT, do not use it
# item.widget.setParent(None)
item.widget.setWindowTitle(item.name)
item.widget.closeEvent = lambda event: self.onPluginWindowClose(item, parent)
item.widget.show()
def onPluginWindowClose(self, item, parent):
self.recoverTab(item, parent)
def recoverTab(self, item, parent):
def add(i):
self.tabWidget.insertTab(i, item.widget, item.name)
self.tabWidget.setCurrentIndex(i)
item.widget.setWindowFlag(Qt.Window, False)
# item.widget.setParent(parent)
status = item.plugin.getConnStatus()
self.setTabIcon(status, i)
# prevent close and add this widget to tab
insertIdx = self.tabWidget.count()
idx = self.items.index(item)
for i in range(self.tabWidget.count()):
widget = self.tabWidget.widget(i)
for _item in self.items:
if _item.widget == widget:
idx2 = self.items.index(_item)
break
if idx2 > idx:
insertIdx = i
break
add(insertIdx)
def updateStyle(self, widget):
self.frameWidget.style().unpolish(widget)
self.frameWidget.style().polish(widget)
self.frameWidget.update()
def onLanguageChanged(self):
idx = self.languageCombobox.currentIndex()
locale = list(self.languages.keys())[idx]
self.config["locale"] = locale
i18n.set_locale(locale)
reply = QMessageBox.question(self, _('Restart now?'),
_("language changed to: ") self.languages[self.config["locale"]] "\n" _("Restart software to take effect now?"), QMessageBox.Yes |
QMessageBox.No, QMessageBox.No)
if reply == QMessageBox.Yes:
self.needRestart = True
self.close()
def onReloadWindow(self, title, msg, callback):
if not title:
title = _('Restart now?')
reply = QMessageBox.question(self, title, msg,
QMessageBox.Yes |
QMessageBox.No, QMessageBox.No)
if reply == QMessageBox.Yes:
callback(True)
self.needRestart = True
self.close()
else:
callback(False)
def MoveToCenter(self):
qr = self.frameGeometry()
cp = QDesktopWidget().availableGeometry().center()
qr.moveCenter(cp)
self.move(qr.topLeft())
def showHint(self, info_type: str, title: str, msg: str):
if info_type == "info":
QMessageBox.information(self, title, msg)
elif info_type == "warning":
QMessageBox.warning(self, title, msg)
elif info_type == "error":
QMessageBox.critical(self, title, msg)
def closeEvent(self, event):
if self.closeTimerId:
event.accept()
return
print("----- close event")
# reply = QMessageBox.question(self, 'Sure To Quit?',
# "Are you sure to quit?", QMessageBox.Yes |
# QMessageBox.No, QMessageBox.No)
if 1: # reply == QMessageBox.Yes:
self.receiveProgressStop = True
# inform plugins
for item in self.items:
item.onDel()
self.saveConfig()
# actual exit after 500ms
self.closeTimerId = self.startTimer(500)
self.setWindowTitle(_("Closing ..."))
self.titleBar.setTitle(_("Closing ..."))
self.setEnabled(False)
if self.helpWindow:
self.helpWindow.close()
event.ignore()
else:
event.ignore()
def timerEvent(self, e):
if self.closeTimerId:
log.i("Close window")
self.killTimer(self.closeTimerId)
self.close()
def saveConfig(self):
# print("save config:", self.config)
self.config.save(parameters.configFilePath)
print("save config compelte")
def uiLoadConfigs(self):
# language
try:
idx = list(self.languages.keys()).index(self.config["locale"])
except Exception:
idx = 0
self.languageCombobox.setCurrentIndex(idx)
# skin
try:
idx = utils_ui.get_skins().index(self.config["skin"])
except Exception:
idx = 0
self.skinButton.setCurrentIndex(idx)
# encoding
self.encodingCombobox.setCurrentIndex(self.supportedEncoding.index(self.config["encoding"]))
def keyPressEvent(self, event):
CustomTitleBarWindowMixin.keyPressEvent(self, event)
item = self.getCurrentItem()
item.onKeyPressEvent(event)
def keyReleaseEvent(self,event):
CustomTitleBarWindowMixin.keyReleaseEvent(self, event)
item = self.getCurrentItem()
item.onKeyReleaseEvent(event)
def getCurrentItem(self, idx = False):
widget = self.tabWidget.currentWidget()
for item in self.items:
if item.widget == widget:
if idx:
return self.tabWidget.indexOf(widget), item
return item
def toggleSettings(self):
widget = self.getCurrentItem().settingWidget
if widget.isVisible():
self.hideSettings()
else:
self.showSettings()
def showSettings(self):
widget = self.getCurrentItem().settingWidget
widget.show()
self.settingsButton.setStyleSheet(
parameters.strStyleShowHideButtonLeft.replace("$DataPath",self.DataPath))
def hideSettings(self):
widget = self.getCurrentItem().settingWidget
widget.hide()
self.settingsButton.setStyleSheet(
parameters.strStyleShowHideButtonRight.replace("$DataPath", self.DataPath))
def toggleFunctional(self):
widget = self.getCurrentItem().functionalWidget
if widget is None:
return
if widget.isVisible():
self.hideFunctional()
else:
self.showFunctional()
def showFunctional(self):
widget = self.getCurrentItem().functionalWidget
if not widget is None:
widget.show()
self.functionalButton.setStyleSheet(
parameters.strStyleShowHideButtonRight.replace("$DataPath",self.DataPath))
def hideFunctional(self):
widget = self.getCurrentItem().functionalWidget
if not widget is None:
widget.hide()
self.functionalButton.setStyleSheet(
parameters.strStyleShowHideButtonLeft.replace("$DataPath", self.DataPath))
def skinChange(self):
idx = self.skinButton.currentIndex()
skin = utils_ui.get_skins()[idx]
file = open(self.DataPath '/assets/qss/style-{}.qss'.format(skin), "r", encoding="utf-8")
self.app.setStyleSheet(file.read().replace("$DataPath", self.DataPath))
utils_ui.setSkin(skin)
self.config["skin"] = skin
def showAbout(self):
help = helpAbout.HelpInfo()
pluginsHelp = {
_("About"): help
}
for p in self.pluginClasses:
if not p.help is None:
pluginsHelp[p.name] = p.help
iconPath = self.DataPath "/" parameters.appIcon
self.helpWindow = HelpWidget(pluginsHelp, icon = iconPath)
self.helpWindow.closed.connect(self.helpWindowClosed)
self.eventFilter.listenWindow(self.helpWindow)
def helpWindowClosed(self):
self.eventFilter.unlistenWindow(self.helpWindow)
self.helpWindow = None
# self.helpWindow
def showUpdate(self, versionInfo):
versionInt = versionInfo.int()
if self.config["skipVersion"] and self.config["skipVersion"] >= versionInt:
return
msgBox = QMessageBox()
desc = versionInfo.desc if len(versionInfo.desc) < 300 else versionInfo.desc[:300] " ... "
link = '<a href="https://github.com/Neutree/COMTool/releases">github.com/Neutree/COMTool/releases</a>'
info = '{}<br>{}<br><br>v{}: {}<br><br>{}'.format(_("New versioin detected, please click learn more to download"), link, '{}.{}.{}'.format(versionInfo.major, versionInfo.minor, versionInfo.dev), versionInfo.name, desc)
learn = msgBox.addButton(_("Learn More"), QMessageBox.YesRole)
skip = msgBox.addButton(_("Skip this version"), QMessageBox.YesRole)
nextTime = msgBox.addButton(_("Remind me next time"), QMessageBox.NoRole)
msgBox.setWindowTitle(_("Need update"))
msgBox.setText(info)
result = msgBox.exec_()
if result == 0:
auto = autoUpdate.AutoUpdate()
auto.OpenBrowser()
elif result == 1:
self.config["skipVersion"] = versionInt
def autoUpdateDetect(self):
auto = autoUpdate.AutoUpdate()
needUpdate, versionInfo = auto.detectNewVersion()
if needUpdate:
self.updateSignal.emit(versionInfo)
def load_fonts(paths):
from PyQt5 import QtGui
for path in paths:
id = QtGui.QFontDatabase.addApplicationFont(path)
fonts = QtGui.QFontDatabase.applicationFontFamilies(id)
print("load fonts:", fonts)
class Splash(QSplashScreen):
'''
show splash when window is loading
'''
def __init__(self, app) -> None:
super().__init__(QPixmap(os.path.join(parameters.assetsDir, "logo.png")))
self.app = app
self.exit = False
self.show()
t = threading.Thread(target=self._processEventsProcess)
t.setDaemon(True)
t.start()
def event(self, e):
if type(e) == QCloseEvent:
self.exit = True
return super().event(e)
def finish(self, w):
self.exit = True
return super().finish(w)
def _processEventsProcess(self):
while not self.exit:
self.app.processEvents()
time.sleep(0.001)
def main():
'''
@retval None: need restart
0: exit ok
others: exit error
'''
ret = 1
try:
QCoreApplication.setAttribute(Qt.AA_EnableHighDpiScaling)
app = QApplication(sys.argv)
splash = Splash(app)
eventFilter = EventFilter()
mainWindow = MainWindow(app, eventFilter, programConfig)
eventFilter.listenWindow(mainWindow)
app.installEventFilter(eventFilter)
g_all_windows.append(mainWindow)
# path = os.path.join(mainWindow.DataPath, "assets", "fonts", "JosefinSans-Regular.ttf")
# load_fonts([path])
log.i("data path:" mainWindow.DataPath)
file = open(mainWindow.DataPath '/assets/qss/style-{}.qss'.format(mainWindow.config["skin"]),"r", encoding="utf-8")
qss = file.read().replace("$DataPath",mainWindow.DataPath)
app.setStyleSheet(qss)
t = threading.Thread(target=mainWindow.autoUpdateDetect)
t.setDaemon(True)
t.start()
splash.finish(mainWindow)
ret = app.exec_()
if mainWindow.needRestart:
ret = None
else:
app.removeEventFilter(eventFilter)
print("-- no need to restart, now exit")
except Exception as e:
import traceback
exc = traceback.format_exc()
show_error(_("Error"), exc)
return ret
def show_error(title, msg):
print("error:", msg)
app = QApplication(sys.argv)
window = QMainWindow()
QMessageBox.information(window, title, msg)
小贴士
感谢您为本站写下的评论,您的评论对其它用户来说具有重要的参考价值,所以请认真填写。
- 类似“顶”、“沙发”之类没有营养的文字,对勤劳贡献的楼主来说是令人沮丧的反馈信息。
- 相信您也不想看到一排文字/表情墙,所以请不要反馈意义不大的重复字符,也请尽量不要纯表情的回复。
- 提问之前请再仔细看一遍楼主的说明,或许是您遗漏了。
- 请勿到处挖坑绊人、招贴广告。既占空间让人厌烦,又没人会搭理,于人于己都无利。
关于好例子网
本站旨在为广大IT学习爱好者提供一个非营利性互相学习交流分享平台。本站所有资源都可以被免费获取学习研究。本站资源来自网友分享,对搜索内容的合法性不具有预见性、识别性、控制性,仅供学习研究,请务必在下载后24小时内给予删除,不得用于其他任何用途,否则后果自负。基于互联网的特殊性,平台无法对用户传输的作品、信息、内容的权属或合法性、安全性、合规性、真实性、科学性、完整权、有效性等进行实质审查;无论平台是否已进行审查,用户均应自行承担因其传输的作品、信息、内容而可能或已经产生的侵权或权属纠纷等法律责任。本站所有资源不代表本站的观点或立场,基于网友分享,根据中国法律《信息网络传播权保护条例》第二十二与二十三条之规定,若资源存在侵权或相关问题请联系本站客服人员,点此联系我们。关于更多版权及免责申明参见 版权及免责申明
网友评论
我要评论