QML QT导入CSV文件并使用python在Tableview中显示

Posted

技术标签:

【中文标题】QML QT导入CSV文件并使用python在Tableview中显示【英文标题】:QML QT import CSV file and display in Tableview using python 【发布时间】:2021-01-06 13:10:49 【问题描述】:

我能够加载 csv 文件并将其显示为 Python QML QT GUI 中的 Tableview。 示例 tabview.py 如下

from os.path import dirname, realpath, join
from PySide2.QtWidgets import QApplication, QWidget
from PySide2.QtCore import QFile
from PySide2.QtUiTools import QUiLoader
from PySide2.QtUiTools import *
from PySide2.QtCore import QFile
from PySide2.QtUiTools import *
import numpy as np
import pandas as pd

scriptDir = dirname(realpath(__file__))

class DataFrameModel(QtCore.QAbstractTableModel):
    DtypeRole = QtCore.Qt.UserRole + 1000
    ValueRole = QtCore.Qt.UserRole + 1001

def __init__(self, df=pd.DataFrame(), parent=None):
    super(DataFrameModel, self).__init__(parent)
    self._dataframe = df

def setDataFrame(self, dataframe):
    self.beginResetModel()
    self._dataframe = dataframe.copy()
    self.endResetModel()

def dataFrame(self):
    return self._dataframe

dataFrame = QtCore.pyqtProperty(pd.DataFrame, fget=dataFrame, fset=setDataFrame)

@QtCore.pyqtSlot(int, QtCore.Qt.Orientation, result=str)
def headerData(self, section: int, orientation: QtCore.Qt.Orientation, role: int = QtCore.Qt.DisplayRole):
    if role == QtCore.Qt.DisplayRole:
        if orientation == QtCore.Qt.Horizontal:
            return self._dataframe.columns[section]
        else:
            return str(self._dataframe.index[section])
    return QtCore.QVariant()

def rowCount(self, parent=QtCore.QModelIndex()):
    if parent.isValid():
        return 0
    return len(self._dataframe.index)

def columnCount(self, parent=QtCore.QModelIndex()):
    if parent.isValid():
        return 0
    return self._dataframe.columns.size

def data(self, index, role=QtCore.Qt.DisplayRole):
    if not index.isValid() or not (0 <= index.row() < self.rowCount() \
        and 0 <= index.column() < self.columnCount()):
        return QtCore.QVariant()
    row = self._dataframe.index[index.row()]
    col = self._dataframe.columns[index.column()]
    dt = self._dataframe[col].dtype

    val = self._dataframe.iloc[row][col]
    if role == QtCore.Qt.DisplayRole:
        return str(val)
    elif role == DataFrameModel.ValueRole:
        return val
    if role == DataFrameModel.DtypeRole:
        return dt
    return QtCore.QVariant()

def roleNames(self):
    roles = 
        QtCore.Qt.DisplayRole: b'display',
        DataFrameModel.DtypeRole: b'dtype',
        DataFrameModel.ValueRole: b'value'
    
    return roles

if __name__ == "__main__":
   import os
   import sys

   app = QtGui.QGuiApplication(sys.argv)
   path = "C:/Users/kalya/Documents/untitled7/tele.csv"
   df = pd.read_csv(path)
   print(df)
   model = DataFrameModel(df)
   engine = QtQml.QQmlApplicationEngine()
   engine.rootContext().setContextProperty("table_model", model)
   qml_path = os.path.join(os.path.dirname(__file__), "main.qml")
   engine.load(QtCore.QUrl.fromLocalFile(qml_path))
   if not engine.rootObjects():
       sys.exit(-1)
   engine.quit.connect(app.quit)
   sys.exit(app.exec_())

这是 tabview.qml

import QtQuick 2.12
import QtQuick.Controls 2.4
import QtQuick.Window 2.11

Window 
   visible: true
   width: 800
   height: 480
   title: qsTr("Load CSV")
   color: '#222222'

TableView 
    id: tableView

    columnWidthProvider: function (column)  return 100; 
    rowHeightProvider: function (column)  return 60; 
    anchors.fill: parent
    leftMargin: rowsHeader.implicitWidth
    topMargin: columnsHeader.implicitHeight
    model: table_model
    delegate: Rectangle 
        color: parseFloat(display) > 100 ? 'grey' : 'black'
        Text 
            text: display
            anchors.fill: parent
            anchors.margins: 10
            color: 'white'
            font.pixelSize: 10
            verticalAlignment: Text.AlignVCenter
        
    
    Rectangle  // mask the headers
        z: 3
        color: "#222222"
        y: tableView.contentY
        x: tableView.contentX
        width: tableView.leftMargin
        height: tableView.topMargin
    

    Row 
        id: columnsHeader
        y: tableView.contentY
        z: 2
        Repeater 
            model: tableView.columns > 0 ? tableView.columns : 1
            Label 
                width: tableView.columnWidthProvider(modelData)
                height: 35
                text: table_model.headerData(modelData, Qt.Horizontal)
                color: '#aaaaaa'
                font.pixelSize: 15
                padding: 10
                verticalAlignment: Text.AlignVCenter

                background: Rectangle  color: "#333333" 
            
        
    
    Column 
        id: rowsHeader
        x: tableView.contentX
        z: 2
        Repeater 
            model: tableView.rows > 0 ? tableView.rows : 1
            Label 
                width: 40
                height: tableView.rowHeightProvider(modelData)
                text: table_model.headerData(modelData, Qt.Vertical)
                color: '#aaaaaa'
                font.pixelSize: 15
                padding: 10
                verticalAlignment: Text.AlignVCenter

                background: Rectangle  color: "#333333" 
            
         
     

      ScrollIndicator.horizontal: ScrollIndicator  
      ScrollIndicator.vertical: ScrollIndicator  
   
 

当我使用与在 stackview 中显示相同的上述文件时,将 ma​​in.pyma​​in.qml 重命名为 tabview.pytabview.qml 并从下面的 ma​​in.qml 加载它们

import QtQuick 2.15
import QtQuick.Window 2.15
import QtQuick.Controls 2.15
import QtGraphicalEffects 1.15

Window 
   id: window
   width: 640
   height: 480
   visible: true
   color: "#000000"
   title: qsTr("Hello World")

Button 
    id: btnLoad
    text: qsTr("Main")
    anchors.left: parent.left
    anchors.right: stackView.left
    anchors.top: parent.top
    anchors.rightMargin: 0
    anchors.leftMargin: 0
    anchors.topMargin: 8
    onClicked: 
        stackView.push(Qt.resolvedUrl("settingsPage.qml"))
                                

Button 
    id: btnmain
    text: qsTr("Load")
    anchors.left: parent.left
    anchors.right: stackView.left
    anchors.top: parent.top
    anchors.rightMargin: 0
    anchors.leftMargin: 0
    anchors.topMargin: 66
    onClicked: stackView.push(Qt.resolvedUrl("tabview.qml"))


StackView 
    id: stackView
    x: 112
    width: 510
    anchors.right: parent.right
    anchors.top: parent.top
    anchors.bottom: parent.bottom
    anchors.bottomMargin: 0
    anchors.rightMargin: 0
    anchors.topMargin: 0
    initialItem: Qt.resolvedUrl("settingsPage.qml")

Connections
         target:backend

csv 无法加载。它抛出错误 file:///C:/Users/luffy/Documents/QTapp/qml/pages/tabview.qml:17: ReferenceError: table_model is not defined

ma​​in.py如下

import sys
import os
import datetime
from os.path import dirname, realpath, join
from PySide2.QtWidgets import QApplication, QWidget
from PySide2.QtCore import QFile
from PySide2.QtUiTools import *
from PyQt5 import QtCore, QtGui, QtQml
from PySide2.QtCore import QFile
from PySide2.QtUiTools import *
import numpy as np
import pandas as pd
import tabview

from PySide2.QtGui import QGuiApplication
from PySide2.QtQml import QQmlApplicationEngine
from PySide2.QtCore import QObject, Slot, Signal, QTimer, QUrl

from PySide2.QtWidgets import QApplication, QMainWindow
from PySide2.QtCore import QFile

class MainWindow(QObject):
def __init__(self):
        QObject.__init__(self)


if __name__ == "__main__":
   app = QGuiApplication(sys.argv)
   engine = QQmlApplicationEngine()
   engine.load(os.path.join(os.path.dirname(__file__), "main.qml"))

main = MainWindow()
engine.rootContext().setContextProperty("backend", main)

if not engine.rootObjects():
    sys.exit(-1)
sys.exit(app.exec_())

我做错了什么? 有没有其他方法可以使用 python 加载 csv 并在 QML QT GUI 中显示?

【问题讨论】:

请提供minimal reproducible example 同时删除from PyQt5 import QtCore, QtGui, QtQml 我要求您提供 MRE,因为您的代码似乎正确(模型和您导出它的方式)所以问题出在您未提供的代码部分 有没有办法捕获其他 .qml 和 .py 文件的输出并将其作为 stackview 发布到 main.qml 文件中? 不,那不是MRE,显然代码甚至无法执行,因为有许多未定义的元素,例如main.py中的stackView没有定义 【参考方案1】:

您的代码有以下错误:

仅执行if __name__ == "__main__": 代码之一(有关详细信息,请阅读here),因此如果您执行主文件,则导出模型的代码将不会执行,因此无法识别如所示通过错误消息。

您不应将 PyQt5 和 PySide2 库结合起来,因为您将遇到难以跟踪的静默错误。

您必须改进导入,因为它们也是难以调试的错误来源。

StackView 页面不应该有一个作为 root 的窗口,而是一个 Item。

考虑到上述情况,那么解决方案是:

├── main.py
├── models.py
├── qml
│   ├── main.qml
│   └── pages
│       ├── settingsPage.qml
│       └── tabview.qml
└── test.csv

ma​​in.py

import os.path
import sys

from PySide2.QtGui import QGuiApplication
from PySide2.QtQml import QQmlApplicationEngine

import pandas as pd

from models import DataFrameModel


CURRENT_DIR = os.path.dirname(os.path.realpath(__file__))

if __name__ == "__main__":
    app = QGuiApplication(sys.argv)

    csv_path = os.path.join(CURRENT_DIR, "test.csv")
    df = pd.read_csv(csv_path)
    model = DataFrameModel(df)

    engine = QQmlApplicationEngine()
    engine.rootContext().setContextProperty("table_model", model)
    engine.load(os.path.join(CURRENT_DIR, "qml", "main.qml"))

    if not engine.rootObjects():
        sys.exit(-1)
    sys.exit(app.exec_())

models.py

import pandas as pd

from PySide2.QtCore import Property, QAbstractTableModel, QModelIndex, Qt, Slot


class DataFrameModel(QAbstractTableModel):
    DtypeRole = Qt.UserRole + 1000
    ValueRole = Qt.UserRole + 1001

    def __init__(self, df=pd.DataFrame(), parent=None):
        super(DataFrameModel, self).__init__(parent)
        self._dataframe = df

    def setDataFrame(self, dataframe):
        self.beginResetModel()
        self._dataframe = dataframe.copy()
        self.endResetModel()

    def dataFrame(self):
        return self._dataframe

    dataFrame = Property(pd.DataFrame, fget=dataFrame, fset=setDataFrame)

    @Slot(int, Qt.Orientation, result=str)
    def headerData(
        self,
        section: int,
        orientation: Qt.Orientation,
        role: int = Qt.DisplayRole,
    ):
        if role == Qt.DisplayRole:
            if orientation == Qt.Horizontal:
                return self._dataframe.columns[section]
            else:
                return str(self._dataframe.index[section])

    def rowCount(self, parent=QModelIndex()):
        if parent.isValid():
            return 0
        return len(self._dataframe.index)

    def columnCount(self, parent=QModelIndex()):
        if parent.isValid():
            return 0
        return self._dataframe.columns.size

    def data(self, index, role=Qt.DisplayRole):
        if not index.isValid() or not (
            0 <= index.row() < self.rowCount()
            and 0 <= index.column() < self.columnCount()
        ):
            return
        row = self._dataframe.index[index.row()]
        col = self._dataframe.columns[index.column()]
        dt = self._dataframe[col].dtype

        val = self._dataframe.iloc[row][col]
        if role == Qt.DisplayRole:
            return str(val)
        elif role == DataFrameModel.ValueRole:
            return val
        if role == DataFrameModel.DtypeRole:
            return dt

    def roleNames(self):
        roles = 
            Qt.DisplayRole: b"display",
            DataFrameModel.DtypeRole: b"dtype",
            DataFrameModel.ValueRole: b"value",
        
        return roles

ma​​in.qml

import QtQuick 2.15
import QtQuick.Window 2.15
import QtQuick.Controls 2.15
import QtGraphicalEffects 1.15

Window 
   id: window
   width: 640
   height: 480
   visible: true
   color: "#000000"
   title: qsTr("Hello World")

    Button 
        id: btnLoad
        text: qsTr("Main")
        anchors.left: parent.left
        anchors.right: stackView.left
        anchors.top: parent.top
        anchors.rightMargin: 0
        anchors.leftMargin: 0
        anchors.topMargin: 8
        onClicked: 
            stackView.push(Qt.resolvedUrl("pages/settingsPage.qml"))
        
    
    Button 
        id: btnmain
        text: qsTr("Load")
        anchors.left: parent.left
        anchors.right: stackView.left
        anchors.top: parent.top
        anchors.rightMargin: 0
        anchors.leftMargin: 0
        anchors.topMargin: 66
        onClicked: stackView.push(Qt.resolvedUrl("pages/tabview.qml"))
    

    StackView 
        id: stackView
        x: 112
        width: 510
        anchors.right: parent.right
        anchors.top: parent.top
        anchors.bottom: parent.bottom
        anchors.bottomMargin: 0
        anchors.rightMargin: 0
        anchors.topMargin: 0
        initialItem: Qt.resolvedUrl("pages/settingsPage.qml")
    

tabview.qml

import QtQuick 2.12
import QtQuick.Controls 2.4

Item 
   width: 800
   height: 480

    TableView 
        id: tableView

        columnWidthProvider: function (column)  return 100; 
        rowHeightProvider: function (column)  return 60; 
        anchors.fill: parent
        leftMargin: rowsHeader.implicitWidth
        topMargin: columnsHeader.implicitHeight
        model: table_model
        delegate: Rectangle 
            color: parseFloat(display) > 100 ? 'grey' : 'black'
            Text 
                text: display
                anchors.fill: parent
                anchors.margins: 10
                color: 'white'
                font.pixelSize: 10
                verticalAlignment: Text.AlignVCenter
            
        
        Rectangle  // mask the headers
            z: 3
            color: "#222222"
            y: tableView.contentY
            x: tableView.contentX
            width: tableView.leftMargin
            height: tableView.topMargin
        

        Row 
            id: columnsHeader
            y: tableView.contentY
            z: 2
            Repeater 
                model: tableView.columns > 0 ? tableView.columns : 1
                Label 
                    width: tableView.columnWidthProvider(modelData)
                    height: 35
                    text: table_model.headerData(modelData, Qt.Horizontal)
                    color: '#aaaaaa'
                    font.pixelSize: 15
                    padding: 10
                    verticalAlignment: Text.AlignVCenter

                    background: Rectangle  color: "#333333" 
                
            
        
        Column 
            id: rowsHeader
            x: tableView.contentX
            z: 2
            Repeater 
                model: tableView.rows > 0 ? tableView.rows : 1
                Label 
                    width: 40
                    height: tableView.rowHeightProvider(modelData)
                    text: table_model.headerData(modelData, Qt.Vertical)
                    color: '#aaaaaa'
                    font.pixelSize: 15
                    padding: 10
                    verticalAlignment: Text.AlignVCenter

                    background: Rectangle  color: "#333333" 
                
             
         

          ScrollIndicator.horizontal: ScrollIndicator  
          ScrollIndicator.vertical: ScrollIndicator  
       
 

【讨论】:

非常感谢它的工作。我导入模型的方式不正确。现在我明白了问题所在。您在第一点中提供的参考链接以及您发布的解决方案帮助了我。谢谢

以上是关于QML QT导入CSV文件并使用python在Tableview中显示的主要内容,如果未能解决你的问题,请参考以下文章

将 Qt 函数导入 QML 文件?

Python 列表更新时更新 ListView

Qt for python 的属性系统使用,并注册python到qml使用

如何为 python 应用程序安装 Qt 模块

C++ 和 QML 中的 QT QML 项

在 qt creator 中从 ..qml 文件生成 .cpp 和 .h 文件