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冻结按钮点击的主要内容,如果未能解决你的问题,请参考以下文章

出现错误:必须先构造一个 QGUIApplication

QObject 子类未检测到 QGuiApplication 事件循环

如何在 QML 文件中直接监听 QGuiApplication::applicationStateChanged 信号

libtorrent-rasterbar 和 QGuiApplication 导致内存损坏

如何在使用 Qt.quit() 而不是整个 QGuiApplication 时只退出当前的 QQmlApplicationEngine?

QGuiApplication没有显示使用QPainter和QPdfWriter