这是关于我的 GUI 应用程序。
您所看到的是我创建的窗口,用于让用户自定义应用程序的样式。我不知道好看不好看,但是我已经尽力按照我的审美让它看起来最好了
窗口分为 9 个区域,每个区域用于更改一组相关小部件的样式。小部件的预览位于每个区域的左侧,每个区域的右侧是滚动区域,其中包含可更改样式某个方面的小部件。
可用于自定义的选项,或者可以更改的小部件样式的属性,如果小部件使用平面背景,则为边框样式、边框颜色、文本颜色和背景颜色,否则为渐变的起始颜色如果背景使用渐变,则为渐变的中间颜色。渐变有 3 个停止点,第一个和最后一个停止点使用相同的颜色。
对于小部件可能处于的所有可能状态中的每个状态,这些属性都可以独立更改。
有两种类型的可以更改样式的小部件。一种类型改变颜色,另一种改变边框样式。边框样式通过 QComboBox 更改,并且可以使用 QLineEdit 和 QColorDialog 更改颜色,通过单击关联的按钮调用该对话框。
每个组控制下划线字典中存储样式配置的一个键,当组的状态改变时,相应的键改变并使用类编译样式,然后更新窗口。
当点击开始按钮时,其文本将变为停止,并且预览区域中的许多小部件将每 125 毫秒更新一次。棋盘的动画是我编写的两个人工智能之间的实时井字游戏。按暂停按钮,它变成恢复按钮,动画暂停。按恢复按钮,动画将恢复。按下停止按钮,一切都会停止并重置。
按下随机化按钮,会生成一个随机样式,并且会立即更新所有更改样式的 96 个组,并应用该样式,如下所示:
它有效,问题是同时更新所有这些小部件会导致卡顿,窗口停止响应几秒钟,然后窗口被更新。我想消除滞后。
由于应用程序的大小,我不会在这里发布完整的代码。另外,由于同时更新 96 个小部件时会出现问题,因此我不会提供最小的可重现示例。该应用程序尚未完成,也未按预期工作,因此我还无法将其发布在代码审查上,事实上它几乎已经完成,当它完成时我会将其发布在代码审查上。
我将展示一些与此问题相关的代码片段,您将无法运行代码,我将完整的项目上传到Google Drive,以便您可以运行它,链接。到目前为止我实现的一切都完全正常,没有错误。
import asyncio
import qasync
import random
import sys
from concurrent.futures import ThreadPoolExecutor
class ColorGetter(Box):
instances = []
def __init__(self, widget: str, key: str, name: str):
super().__init__()
ColorGetter.instances.append(self)
self.config = CONFIG[widget] if widget else CONFIG
self.key = key
self.name = name
self.set_color(self.config[key])
self.init_GUI()
self.button.clicked.connect(self._show_color)
self.picker.accepted.connect(self.pick_color)
self.color_edit.returnPressed.connect(self.edit_color)
def init_GUI(self):
self.color_edit = ColorEdit(self.color_text)
self.button = Button(self.color_text)
self.vbox = make_vbox(self)
self.vbox.addWidget(Label(self.name))
self.vbox.addWidget(self.color_edit)
self.vbox.addWidget(self.button)
self.picker = ColorPicker()
def set_color(self, text: str):
self.color = [int(text[a:b], 16) for a, b in ((1, 3), (3, 5), (5, 7))]
@property
def color_text(self):
r, g, b = self.color
return f"#{r:02x}{g:02x}{b:02x}"
def _show_color(self):
self.picker.setCurrentColor(QColor(*self.color))
self.picker.show()
def _update(self):
self.button.setText(self.color_text)
self.config[self.key] = self.color_text
GLOBALS["Window"].update_style()
def pick_color(self):
self.color = self.picker.currentColor().getRgb()[:3]
self.color_edit.setText(self.color_text)
self.color_edit.color = self.color_text
self._update()
def edit_color(self):
text = self.color_edit.text()
self.color_edit.color = text
self.set_color(text)
self.color_edit.clearFocus()
self._update()
def sync_config(self):
color_html = self.config[self.key]
self.color = [int(color_html[a:b], 16) for a, b in ((1, 3), (3, 5), (5, 7))]
self.color_edit.setText(color_html)
self.color_edit.color = color_html
self.button.setText(color_html)
class BorderStylizer(Box):
instances = []
def __init__(self, widget: str, key: str, name: str):
super().__init__()
BorderStylizer.instances.append(self)
self.config = CONFIG[widget] if widget else CONFIG
self.key = key
self.name = name
self.borderstyle = self.config[key]
self.init_GUI()
def init_GUI(self):
self.vbox = make_vbox(self)
self.vbox.addWidget(Label(self.name))
self.combobox = ComboBox(BORDER_STYLES)
self.vbox.addWidget(self.combobox)
self.combobox.setCurrentText(self.borderstyle)
self.combobox.currentTextChanged.connect(self._update)
def _update(self):
self.borderstyle = self.combobox.currentText()
self.config[self.key] = self.borderstyle
GLOBALS["Window"].update_style()
def sync_config(self):
self.borderstyle = self.config[self.key]
self.combobox.setCurrentText(self.borderstyle)
async def sync_config():
loop = asyncio.get_event_loop()
with ThreadPoolExecutor(max_workers=64) as executor:
await asyncio.gather(
*(
loop.run_in_executor(executor, instance.sync_config)
for instance in ColorGetter.instances + BorderStylizer.instances
)
)
def random_style():
for entry in CONFIG.values():
for k in entry:
entry[k] = (
random.choice(BORDER_STYLES)
if k == "borderstyle"
else f"#{random.randrange(16777216):06x}"
)
asyncio.run(sync_config())
GLOBALS["Window"].update_style()
GLOBALS["Window"].qthread.change.emit()
if __name__ == "__main__":
app = QApplication(sys.argv)
app.setStyle("Fusion")
if sys.platform == "win32":
asyncio.set_event_loop_policy(asyncio.WindowsProactorEventLoopPolicy())
loop = qasync.QEventLoop(app)
asyncio.set_event_loop(loop)
我尝试通过异步运行更新来消除滞后,但效果不太好。
什么是更好的解决方案?
我解决了这个问题。感谢@musicamante 的评论。
所以问题非常简单,只是每次调用
random_style
时,每个组合框的文本都会更改,并且文本更改会导致每个组合框对窗口进行新的额外 update_style
调用。
所以我只是在进行更改之前断开每个组合框的
.currentTextChanged
信号,然后重新连接,如下所示:
def sync_config(self):
self.borderstyle = self.config[self.key]
self.combobox.currentTextChanged.disconnect(self._update)
self.combobox.setCurrentText(self.borderstyle)
self.combobox.currentTextChanged.connect(self._update)
我尝试同步执行,没有发现明显的滞后,所以我摆脱了异步循环。
def random_style():
for entry in CONFIG.values():
for k in entry:
entry[k] = (
random.choice(BORDER_STYLES)
if k == "borderstyle"
else f"#{random.randrange(16777216):06x}"
)
for instance in ColorGetter.instances + BorderStylizer.instances:
instance.sync_config()
GLOBALS["Window"].update_style()
GLOBALS["Window"].qthread.change.emit()