change dynamically data dash in pyqt5

Viewed 803

So I made an App that shows dash graphs inside a pyqt5 which takes data from a table widget. the App fonctions well at the first data but after if I change the data in the table i can't see changes in the graph as well, so can where is the problem occurring?

# -*- coding: utf-8 -*-

# Form implementation generated from reading ui file 'test.ui'
#
# Created by: PyQt5 UI code generator 5.9.2
#
# WARNING! All changes made in this file will be lost!

from PyQt5 import QtCore, QtGui, QtWidgets
import sys
import dash
import dash_core_components as dcc
import dash_html_components as html
import threading
import plotly.figure_factory as ff


class Ui_MainWindow(object):
    def setupUi(self, MainWindow):
        MainWindow.setObjectName("MainWindow")
        MainWindow.resize(956, 701)
        self.centralwidget = QtWidgets.QWidget(MainWindow)
        self.centralwidget.setObjectName("centralwidget")
        self.verticalLayout = QtWidgets.QVBoxLayout(self.centralwidget)
        self.verticalLayout.setObjectName("verticalLayout")
        self.webEngineView = QtWebEngineWidgets.QWebEngineView(self.centralwidget)
        self.webEngineView.setUrl(QtCore.QUrl("http://127.0.0.1:8050"))
        self.webEngineView.setObjectName("webEngineView")
        self.verticalLayout.addWidget(self.webEngineView)
        self.tableWidget = QtWidgets.QTableWidget(self.centralwidget)
        self.tableWidget.setObjectName("tableWidget")
        self.tableWidget.setColumnCount(0)
        self.tableWidget.setRowCount(0)
        self.verticalLayout.addWidget(self.tableWidget)
        self.pushButton = QtWidgets.QPushButton(self.centralwidget)
        self.pushButton.setObjectName("pushButton")
        self.verticalLayout.addWidget(self.pushButton)
        MainWindow.setCentralWidget(self.centralwidget)
        self.menubar = QtWidgets.QMenuBar(MainWindow)
        self.menubar.setGeometry(QtCore.QRect(0, 0, 956, 26))
        self.menubar.setObjectName("menubar")
        MainWindow.setMenuBar(self.menubar)
        self.statusbar = QtWidgets.QStatusBar(MainWindow)
        self.statusbar.setObjectName("statusbar")
        MainWindow.setStatusBar(self.statusbar)
        self.tableWidget.setColumnCount(3)
        self.tableWidget.setRowCount(3)
        self.tableWidget.setHorizontalHeaderLabels(('X', 'Y1','Y2'))
        header = self.tableWidget.horizontalHeader()       
        header.setSectionResizeMode(0, QtWidgets.QHeaderView.Stretch)
        header.setSectionResizeMode(1, QtWidgets.QHeaderView.Stretch)
        header.setSectionResizeMode(2, QtWidgets.QHeaderView.Stretch)
        self.pushButton.clicked.connect(self.Clicked)
        self.retranslateUi(MainWindow)
        QtCore.QMetaObject.connectSlotsByName(MainWindow)
            
    def Clicked(self):

        df=[]
        data = []
        for c in range(self.tableWidget.columnCount()):
            data.append([])
            for r in range(self.tableWidget.rowCount()):
                data[c].append(self.tableWidget.item(r,c).text())
        
        for r in range(self.tableWidget.rowCount()):
            df.append(dict(Task=data[0][r], Start=data[1][r], Finish=data[2][r]))
        print(df)           
        threading.Thread(target=self.run_dash, args=([df]), daemon=True).start()  
        
    def run_dash(self,df):

        fig = ff.create_gantt(df)        
        app = dash.Dash()
        app.layout = html.Div([
            dcc.Graph(figure=fig)
        ])
        
        app.run_server(debug=True, use_reloader=False)

    def retranslateUi(self, MainWindow):
        _translate = QtCore.QCoreApplication.translate
        MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
        self.pushButton.setText(_translate("MainWindow", "PushButton"))

from PyQt5 import QtWebEngineWidgets

if __name__ == "__main__":
    
    app = QtWidgets.QApplication(sys.argv)
    app.setStyle(QtWidgets.QStyleFactory.create('Fusion'))
    Optimizer = QtWidgets.QMainWindow()
    ui = Ui_MainWindow()
    ui.setupUi(Optimizer)
    Optimizer.show()

    sys.exit(app.exec_())

Note : the seconde and third columns are date columns to show a gantt chart. so data entry in those two columns shauld be like: 2020-07-27 | 2020-07-30

1 Answers

Dash launches a server that updates the information but this is rendered by the browser, this means that although the server has updated information on the client side, it must request that updated information and repaint it. This can be done by reloading the page:

import sys
import threading

from PyQt5 import QtCore, QtGui, QtWidgets, QtWebEngineWidgets

import dash
import dash_core_components as dcc
import dash_html_components as html
import plotly.figure_factory as ff


class QDash(QtCore.QObject):
    def __init__(self, parent=None):
        super().__init__(parent)

        self._app = dash.Dash()
        self.app.layout = html.Div()

    @property
    def app(self):
        return self._app

    def update_graph(self, df):
        fig = ff.create_gantt(df)
        self.app.layout = html.Div([dcc.Graph(figure=fig)])

    def run(self, **kwargs):
        threading.Thread(target=self.app.run_server, kwargs=kwargs, daemon=True).start()


class Mainwindow(QtWidgets.QMainWindow):
    def __init__(self, parent=None):
        super().__init__(parent)

        self.browser = QtWebEngineWidgets.QWebEngineView()
        self.table = QtWidgets.QTableWidget()
        self.button = QtWidgets.QPushButton("Press me")

        central_widget = QtWidgets.QWidget()
        self.setCentralWidget(central_widget)
        lay = QtWidgets.QVBoxLayout(central_widget)
        lay.addWidget(self.browser, stretch=1)
        lay.addWidget(self.table, stretch=1)
        lay.addWidget(self.button)

        self.resize(640, 480)

        self.table.setColumnCount(3)
        self.table.setHorizontalHeaderLabels(("X", "Y1", "Y2"))
        header = self.table.horizontalHeader()
        for i in range(self.table.columnCount()):
            header.setSectionResizeMode(i, QtWidgets.QHeaderView.Stretch)

        self.qdask = QDash()
        self.qdask.run(debug=True, use_reloader=False)
        self.browser.load(QtCore.QUrl("http://127.0.0.1:8050"))

        self.button.clicked.connect(self.update_figure)

        current_date = QtCore.QDateTime.currentDateTime()

        for i in range(3):
            self.append_row(
                task="Task{}".format(i),
                start=current_date,
                finish=current_date.addDays(i + 1),
            )

    @QtCore.pyqtSlot()
    def update_figure(self):

        df = []

        for i in range(self.table.rowCount()):
            task = self.table.item(i, 0).data(QtCore.Qt.DisplayRole)
            start = (
                self.table.item(i, 1)
                .data(QtCore.Qt.DisplayRole)
                .toString(QtCore.Qt.ISODateWithMs)
            )
            finish = (
                self.table.item(i, 2)
                .data(QtCore.Qt.DisplayRole)
                .toString(QtCore.Qt.ISODateWithMs)
            )

            d = dict(Task=task, Start=start, Finish=finish)
            df.append(d)

        print(df)

        self.qdask.update_graph(df)
        self.browser.reload()

    def append_row(
        self,
        task="",
        start=QtCore.QDateTime.currentDateTime(),
        finish=QtCore.QDateTime.currentDateTime(),
    ):
        row = self.table.rowCount()
        self.table.insertRow(row)
        for column, value in enumerate((task, start, finish)):
            it = QtWidgets.QTableWidgetItem()
            it.setData(QtCore.Qt.DisplayRole, value)
            self.table.setItem(row, column, it)


if __name__ == "__main__":

    app = QtWidgets.QApplication(sys.argv)
    app.setStyle(QtWidgets.QStyleFactory.create("Fusion"))

    w = Mainwindow()
    w.show()

    sys.exit(app.exec_())
Related