第4章 - 添加一个QTableView

现在你已经有了一个QMainWindow,你可以在你的界面中包含一个centralWidget。通常,在大多数数据驱动的应用程序中,QWidget用于显示数据。使用表格视图来显示你的数据。

第一步是添加一个仅包含QTableView的水平布局。您可以创建一个QTableView对象并将其放置在QHBoxLayout中。一旦QWidget正确构建,将该对象作为中心小部件传递给QMainWindow。

请记住,QTableView 需要一个模型来显示信息。在这种情况下,你可以使用 QAbstractTableModel 实例。

注意

你也可以使用QTableWidget自带的默认项模型。QTableWidget是一个便利类,可以大大减少你的代码量,因为你不需要实现数据模型。然而,它比QTableView灵活性差,因为QTableWidget不能与任何数据一起使用。要了解更多关于Qt的模型-视图框架的信息,请参考模型视图编程 文档。

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

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

这是一个实现CustomTableModel的脚本:

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

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

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

你还需要对第3章中的main_window.pymain.py进行一些小的修改,以便将Widget包含在 MainWindow中。

在以下片段中,您将看到这些更改被突出显示:

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