Pyside2 QGuiApplication,Gui冻结按钮点击
Posted
技术标签:
【中文标题】Pyside2 QGuiApplication,Gui冻结按钮点击【英文标题】:Pyside2 QGuiApplication, Gui freeze on button Clicked 【发布时间】:2020-07-15 16:32:06 【问题描述】:我正在使用 QML、Pyside2 和 QGuiApplication 编写桌面应用程序 问题是当我单击任何按钮 Gui 冻结时,代码执行和 Gui 返回正常状态。 我搜索并发现问题是我的应用程序的单线程。 所以我试着让它成为多线程 我试过了:
QGuiApplication.processEvents()
和:
app = QGuiApplication()
app.processEvents()
在 pyQT5 中,我知道这是答案并解决了问题:
QApplication.processEvents()
正如文档所说 link 函数:
processEvents()
应该工作但不工作!
这是我的 Qml 调用函数:
RoundButton
icon.source :"icons/baseline_play_arrow_black_48dp"
onClicked: DataVisClass.road_analysis()
这是 DataVisClass.py :
from PySide2.QtCore import QObject, Slot
from PySide2.QtGui import QGuiApplication
class DataVis(QObject):
def __init__(self,app):
super().__init__()
self.app = app
self.roadAnalyser = RoadAnalyser()
@Slot()
def road_analysis(self):
self.roadAnalyser.centrality(True, True, 'Piedmont, California, USA', "drive_service", "betweenness_centrality", self.app)
和 RoadAnalyser.py:
class RoadAnalyser:
def centrality(self, logs, use_cache, place, net_type, alg_type, app):
app.processEvents()
QGuiApplication.processEvents()
ox.config(log_console=logs, use_cache=use_cache)
# place = 'Piedmont, California, USA'
G = ox.graph_from_address(place, network_type=net_type)
# G = ox.graph_from_address(place, network_type='drive_service')
gdf = ox.gdf_from_place(place)
area = ox.projection.project_gdf(gdf).unary_union.area
# calculate basic and extended network stats, merge them together, and display
stats = ox.basic_stats(G, area=area)
extended_stats = ox.extended_stats(G, ecc=True, bc=True, cc=True)
for key, value in extended_stats.items():
stats[key] = value
QGuiApplication.processEvents()
pd.Series(stats)
G_projected = ox.project_graph(G)
max_node, max_bc = max(extended_stats[alg_type].items(), key=lambda x: x[1])
# max_node, max_bc = max(extended_stats['betweenness_centrality'].items(), key=lambda x: x[1])
print("Best node is : ",max_bc,max_node)
# nc = get_node_colors_by_stat(G_projected, data=extended_stats['betweenness_centrality'])
nc = get_node_colors_by_stat(G_projected, data=extended_stats[alg_type])
# for each in nc :
# print(each)
fig, ax = ox.plot_graph(G, fig_height=6, node_color=nc, node_size=20, node_zorder=2,
edge_linewidth=2, edge_color='#333333', bgcolor='k')
# for each in extended_stats['betweenness_centrality'].items():
# print(each)
谢谢
【问题讨论】:
请提供minimal reproducible example @eyllanesc 抱歉,这个项目很大,但我的代码路径很小。谢谢 如果你在 PySide 中做线程,你应该确保使用 QThreads。作为线程的替代品,我认为你可以使用 QTimer 中的singleShot()
来做简单的异步执行
@bfris 它只是一个线程和 Gui
@mohsen。抱歉,您提到了多线程。 processEvents()
并没有真正为你做任何多线程。在某些情况下,您可以使用它来确保在需要时刷新窗口。 singleShot
是一种触发独立于 GUI 循环运行的进程的简单方法。我想。
【参考方案1】:
耗时的任务不应该在主线程中运行,因为它们阻塞了事件循环,这样做的一个影响是冻结。解决方案是在另一个线程中运行它:
删除所有与“app”或QXApplication相关的东西,因为它是不必要的
class RoadAnalyser:
def centrality(self, logs, use_cache, place, net_type, alg_type):
ox.config(log_console=logs, use_cache=use_cache)
# place = 'Piedmont, California, USA'
G = ox.graph_from_address(place, network_type=net_type)
# G = ox.graph_from_address(place, network_type='drive_service')
gdf = ox.gdf_from_place(place)
area = ox.projection.project_gdf(gdf).unary_union.area
# calculate basic and extended network stats, merge them together, and display
stats = ox.basic_stats(G, area=area)
extended_stats = ox.extended_stats(G, ecc=True, bc=True, cc=True)
for key, value in extended_stats.items():
stats[key] = value
pd.Series(stats)
G_projected = ox.project_graph(G)
max_node, max_bc = max(extended_stats[alg_type].items(), key=lambda x: x[1])
# max_node, max_bc = max(extended_stats['betweenness_centrality'].items(), key=lambda x: x[1])
print("Best node is : ", max_bc, max_node)
# nc = get_node_colors_by_stat(G_projected, data=extended_stats['betweenness_centrality'])
nc = get_node_colors_by_stat(G_projected, data=extended_stats[alg_type])
# for each in nc :
# print(each)
fig, ax = ox.plot_graph(
G,
fig_height=6,
node_color=nc,
node_size=20,
node_zorder=2,
edge_linewidth=2,
edge_color="#333333",
bgcolor="k",
)
使用threading.Thread
import threading
class DataVis(QObject):
def __init__(self, app):
super().__init__()
self.app = app
self.roadAnalyser = RoadAnalyser()
@Slot()
def road_analysis(self):
threading.Thread(
target=self.roadAnalyser.centrality,
args=(
True,
True,
"Piedmont, California, USA",
"drive_service",
"betweenness_centrality",
),
).start()
更新
如果要从线程发送信息,则必须使用信号
class DataVis(QObject):
fooSignal = Signal(str)
# ...
@Slot()
def road_analysis(self):
threading.Thread(
target=self.roadAnalyser.centrality,
args=(
True,
True,
"Piedmont, California, USA",
"drive_service",
"betweenness_centrality",
self.fooSignal
),
).start()
class RoadAnalyser:
def centrality(self, logs, use_cache, place, net_type, alg_type, signal):
# ...
signal.emit("foo")
Connections
target: DataVisClass
function onFooSignal(msg)
console.log(msg)
【讨论】:
先生。我可以再问你一个问题吗?当函数结束时,我在 RoadAnalyser.py 添加一个 return 语句。我怎么能说 Gui 做这项工作会抛出 python 代码?因为它的多线程现在 GUI 运行,我不能使用 return ? @mohsen 如果你想在线程之间传输信息,那么你必须使用信号 eyllanesc 。你能给我一个在结束函数后将文本设置为标签的例子吗?我是新手,有点难 再次工作!纯粹的好和内容丰富。你帮了我很多.. 谢谢你以上是关于Pyside2 QGuiApplication,Gui冻结按钮点击的主要内容,如果未能解决你的问题,请参考以下文章
QObject 子类未检测到 QGuiApplication 事件循环
如何在 QML 文件中直接监听 QGuiApplication::applicationStateChanged 信号
libtorrent-rasterbar 和 QGuiApplication 导致内存损坏
如何在使用 Qt.quit() 而不是整个 QGuiApplication 时只退出当前的 QQmlApplicationEngine?