第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.py
和
main.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