簡體   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