繁体   English   中英

Dash 在 PyQt QThread() 中运行 - 使用新数据重新初始化的最合适方式

[英]Dash running in a PyQt QThread() - most appropriate way to re-initialize with new data

目前我有一个在 QThread 中运行 Dash 的 PyQt5 应用程序,如下所示:

class MapView(QObject):

    message = pyqtSignal(str)
    shutting_down = pyqtSignal(bool)

    def __init__(self, data):
        super().__init__()
        self.app = dash.Dash(__name__)
        self.data = data
    
        #manual callbacks
        self.app.callback(
            Output('hover-data', 'children'),
            Input('basic-interactions', 'hoverData'))(self.display_hover_data)

        self.app.callback(
            Output('page-content', 'children'),
            Input('url', 'pathname'))(self.shutdown)
        
#...more callbacks

    def shutdown(self, pathname):
        if pathname != '/shutdown':
            return
        print("Trying to shutdown map dash")
        func = request.environ.get('werkzeug.server.shutdown')
        if func is None:
            raise RuntimeError('Not running with the Werkzeug Server')
        func()
        self.shutting_down.emit(True)

#.....lots of stuff like graphs, the layout, a function to start the server etc

然后我的主要 pyqt5 应用程序中的线程代码是:

def show_map(self):
        
        self.mapthread = QThread()
        self.map = map_dash.MapView(self.data) 
        self.map.moveToThread(self.mapthread)
        self.map.message.connect(self.update_output)
        self.map.shutting_down.connect(self.close_map_thread)
        self.mapthread.finished.connect(self.open_project)
        self.mapthread.started.connect(self.map.run)
        self.mapthread.start()
        self.browser.setUrl(QUrl('http://127.0.0.1:8050'))
        self.update_output("Map plotted.")

我最初的问题是,如果我在 Dash 已经运行时尝试使用新数据运行show_map() (当项目已经运行时,用户转到File -> Open ),我会得到致命的Qthread: destroyed while thread is still running 于是我走上了关闭flask服务器,然后关闭线程,再回到打开新项目的函数的路径。 就像这样:

  1. 用户转到File -> Open
  2. def open_project()检查mapthread.isRunning()
  3. 它没有运行所以它打开一个新项目,创建一个新的QThread和新的MapView实例
  4. 用户转到File -> Open再次File -> Open
  5. 签入 (2) 返回 True,因此要求关闭 Flask 服务器
  6. 服务器关闭后,shutting_down 信号导致线程被要求quit() (我不确定这有多健壮,因为它不是来自烧瓶的信号,只是在我要求它之后的一行关闭。但它现在似乎工作)。
  7. 线程完成后,线程发出finished() open_project()再次调用open_project()
  8. open_project这次看到线程没有运行,允许用户打开一个新文件。

4 到 8 的运行时间不会太长,但这一切似乎有点复杂,而且无论出于何种原因,Dash 布局都会出现一些小故障。 就目前而言,当 QThread 在任何其他情况下完成时,它会调用open_project (尽管我可能会解决这个问题)。 有一个更好的方法吗? 我可以以某种方式为现有地图实例提供新数据吗? 我已经阅读了关于dcc.Interval的文档,但这似乎也不是一个很好的方法......

更新(根据下面的评论):

现在我正在做的是将新数据传递给线程self.map.data = new_data ,然后使用 url 上的回调重新绘制地图并刷新布​​局。 就像这样:

    elif pathname == '/refresh':
            self.draw_map()
            self.set_layout()

但问题就在这里。 self.set_layout()使用 OLD 图形刷新破折号。 我已经验证self.draw_map()正在绘制一个新图形。 但是第二次调用 /refresh 确实会导致 dash 使用新数字。 那么 dash 是否将旧图形存储在一个缓存中,并且在几分之一秒内还没有更新,以便再次设置布局? 如何让 dash 等待缓存更新?

一种方法是将新数据传递到 dash 实例

self.map.data = new_data

但这不会刷新布局,您不能简单地调用

self.map.set_layout()来自主线程。 相反,您必须像这样使用 url 回调:

def shutdown(self, pathname):
        if pathname == '/shutdown':
            print("Trying to shutdown map dash")
            func = request.environ.get('werkzeug.server.shutdown')
            if func is None:
                raise RuntimeError('Not running with the Werkzeug Server')
            func()
            self.shutting_down.emit(True)
        elif pathname == '/refresh':
            self.draw_map()
            self.set_layout()

当然,现在将该方法称为像handle_url()这样的方法会更合适。

但还有一个问题。 我猜是由于某些缓存问题,除非用户再次手动调用刷新,否则 dash 会显示该图的旧版本。 解决方案是添加一个dcc.Interval回调来更新图形(它调用draw_map() )。 不需要每秒都执行此操作,例如,您可以将间隔设置为 999999。 事实上,您可以设置 'max_intervals=1' 并且它仍然有效。 只要有回调,就会在页面第一次刷新时调用,并更新图形。

事实上,有了这个, draw_map()set_layout()函数甚至不需要在 url 回调中调用。 所以代码现在看起来像这样:

def shutdown(self, pathname):
        if pathname == '/shutdown':
            print("Trying to shutdown map dash")
            func = request.environ.get('werkzeug.server.shutdown')
            if func is None:
                raise RuntimeError('Not running with the Werkzeug Server')
            func()
            self.shutting_down.emit(True)
        elif pathname == '/refresh':
            print("I'm doing nothing")

它有效。 但是 url 回调本身是必要的。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM