[英]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服務器,然后關閉線程,再回到打開新項目的函數的路徑。 就像這樣:
File -> Open
def open_project()
檢查mapthread.isRunning()
QThread
和新的MapView
實例File -> Open
再次File -> Open
quit()
(我不確定這有多健壯,因為它不是來自燒瓶的信號,只是在我要求它之后的一行關閉。但它現在似乎工作)。finished()
open_project()
再次調用open_project()
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.