第4章 - 添加一个QTableView#

现在您已经有了QMainWindow,您可以将centralWidget添加到您的界面中。通常,QWidget用于在大多数数据驱动应用程序中显示数据。使用表格视图来显示您的数据。

第一步是添加一个只有一个QTableView的水平布局。您可以使用一个QTableView对象,并将其放置在一个QHBoxLayout中。一旦QWidget被正确构建,将其对象传递给QMainWindow作为其中央部件。

请注意,QTableView需要一个模型来显示信息。在本例中,您可以使用一个QAbstractTableModel实例。

注意

您还可以使用QTableWidget随附的默认项模型。QTableWidget是一个减少您的代码库的便利类,您不需要实现数据模型。但是,它比QTableView不那么灵活,因为QTableWidget不能与任何数据一起使用。有关Qt模型-视图框架的更多信息,请参阅文档<https://doc.qt.ac.cn/qt-5/model-view-programming.html>

为QTableView实现模型,允许您:- 设置表头,- 操作单元格值的格式(请记住我们有UTC时间和浮点数),- 设置样式属性,如文本对齐,- 以及设置单元格或其内容的颜色属性。

要子类化QAbstractTable,您必须重新实现其虚拟方法rowCount(),columnCount()和data()。这样,您可以确保数据得到正确处理。此外,重新实现headerData()方法,为视图提供表头信息。

以下是实现CustomTableModel的脚本

 1
 2from PySide6.QtCore import Qt, QAbstractTableModel, QModelIndex
 3from PySide6.QtGui import QColor
 4
 5
 6class CustomTableModel(QAbstractTableModel):
 7    def __init__(self, data=None):
 8        QAbstractTableModel.__init__(self)
 9        self.load_data(data)
10
11    def load_data(self, data):
12        self.input_dates = data[0].values
13        self.input_magnitudes = data[1].values
14
15        self.column_count = 2
16        self.row_count = len(self.input_magnitudes)
17
18    def rowCount(self, parent=QModelIndex()):
19        return self.row_count
20
21    def columnCount(self, parent=QModelIndex()):
22        return self.column_count
23
24    def headerData(self, section, orientation, role):
25        if role != Qt.DisplayRole:
26            return None
27        if orientation == Qt.Horizontal:
28            return ("Date", "Magnitude")[section]
29        else:
30            return f"{section}"
31
32    def data(self, index, role=Qt.DisplayRole):
33        column = index.column()
34        row = index.row()
35
36        if role == Qt.DisplayRole:
37            if column == 0:
38                date = self.input_dates[row].toPython()
39                return str(date)[:-3]
40            elif column == 1:
41                magnitude = self.input_magnitudes[row]
42                return f"{magnitude:.2f}"
43        elif role == Qt.BackgroundRole:
44            return QColor(Qt.white)
45        elif role == Qt.TextAlignmentRole:
46            return Qt.AlignRight
47
48        return None
49

现在,创建一个包含QTableView的QWidget,并将其连接到您的CustomTableModel。

 1
 2from PySide6.QtWidgets import (QHBoxLayout, QHeaderView, QSizePolicy,
 3                               QTableView, QWidget)
 4
 5from table_model import CustomTableModel
 6
 7
 8class Widget(QWidget):
 9    def __init__(self, data):
10        QWidget.__init__(self)
11
12        # Getting the Model
13        self.model = CustomTableModel(data)
14
15        # Creating a QTableView
16        self.table_view = QTableView()
17        self.table_view.setModel(self.model)
18
19        # QTableView Headers
20        self.horizontal_header = self.table_view.horizontalHeader()
21        self.vertical_header = self.table_view.verticalHeader()
22        self.horizontal_header.setSectionResizeMode(
23                               QHeaderView.ResizeToContents
24                               )
25        self.vertical_header.setSectionResizeMode(
26                             QHeaderView.ResizeToContents
27                             )
28        self.horizontal_header.setStretchLastSection(True)
29
30        # QWidget Layout
31        self.main_layout = QHBoxLayout()
32        size = QSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred)
33
34        ## Left layout
35        size.setHorizontalStretch(1)
36        self.table_view.setSizePolicy(size)
37        self.main_layout.addWidget(self.table_view)
38
39        # Set the layout to the QWidget
40        self.setLayout(self.main_layout)
41

您还需要对第3章中的 main_window.pymain.py 进行一些小改动,以确保将Widget包含在MainWindow内。

以下片段中将突出显示这些更改。

 1
 2from PySide6.QtCore import Slot
 3from PySide6.QtGui import QAction, QKeySequence
 4from PySide6.QtWidgets import QMainWindow
 5
 6
 7class MainWindow(QMainWindow):
 8    def __init__(self, widget):
 9        QMainWindow.__init__(self)
10        self.setWindowTitle("Eartquakes information")
11        self.setCentralWidget(widget)
12        # Menu
13        self.menu = self.menuBar()
14        self.file_menu = self.menu.addMenu("File")
15
16        ## Exit QAction
17        exit_action = QAction("Exit", self)
18        exit_action.setShortcut(QKeySequence.Quit)
19        exit_action.triggered.connect(self.close)
20
21        self.file_menu.addAction(exit_action)
22
23        # Status Bar
24        self.status = self.statusBar()
25        self.status.showMessage("Data loaded and plotted")
26
27        # Window dimensions
28        geometry = self.screen().availableGeometry()
29        self.setFixedSize(geometry.width() * 0.8, geometry.height() * 0.7)
30
 1
 2import sys
 3import argparse
 4import pandas as pd
 5
 6from PySide6.QtCore import QDateTime, QTimeZone
 7from PySide6.QtWidgets import QApplication
 8from main_window import MainWindow
 9from main_widget import Widget
10
11
12def transform_date(utc, timezone=None):
13    utc_fmt = "yyyy-MM-ddTHH:mm:ss.zzzZ"
14    new_date = QDateTime().fromString(utc, utc_fmt)
15    if timezone:
16        new_date.setTimeZone(timezone)
17    return new_date
18
19
20def read_data(fname):
21    # Read the CSV content
22    df = pd.read_csv(fname)
23
24    # Remove wrong magnitudes
25    df = df.drop(df[df.mag < 0].index)
26    magnitudes = df["mag"]
27
28    # My local timezone
29    timezone = QTimeZone(b"Europe/Berlin")
30
31    # Get timestamp transformed to our timezone
32    times = df["time"].apply(lambda x: transform_date(x, timezone))
33
34    return times, magnitudes
35
36
37if __name__ == "__main__":
38    options = argparse.ArgumentParser()
39    options.add_argument("-f", "--file", type=str, required=True)
40    args = options.parse_args()
41    data = read_data(args.file)
42
43    # Qt Application
44    app = QApplication(sys.argv)
45
46    widget = Widget(data)
47    window = MainWindow(widget)
48    window.show()
49
50    sys.exit(app.exec())
51