在pyqt5中动态更改数据破折号

Posted

技术标签:

【中文标题】在pyqt5中动态更改数据破折号【英文标题】:change dynamically data dash in pyqt5 【发布时间】:2020-07-27 12:37:04 【问题描述】:

所以我制作了一个应用程序,它在 pyqt5 中显示虚线图,它从表格小部件中获取数据。应用程序在第一个数据上表现良好,但如果我更改表中的数据,我也无法在图表中看到变化,那么问题出在哪里?

# -*- 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_())

注意:第二列和第三列是显示甘特图的日期列。所以这两列中的数据输入应该是:2020-07-27 | 2020-07-30

【问题讨论】:

【参考方案1】:

Dash 启动一个更新信息的服务器,但这是由浏览器呈现的,这意味着尽管服务器在客户端更新了信息,但它必须请求更新的信息并重新绘制它。这可以通过重新加载页面来完成:

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_())

【讨论】:

以上是关于在pyqt5中动态更改数据破折号的主要内容,如果未能解决你的问题,请参考以下文章

在 PyQt5 中动态更改 GUI 元素?

PyQt5 动态更改应用程序主题/调色板

在烧瓶 wtforms jinja select 上设置动态数据属性

在 pyqt5 界面中动态更新 matplotlib 画布

在小部件 PyQt5 上设置布局后更改布局

如何在绘图破折号中更改图像大小