Chapter 4 - Add a QTableView¶
Now that you have a QMainWindow, you can include a centralWidget to your interface. Usually, a QWidget is used to display data in most data-driven applications. Use a table view to display your data.
The first step is to add a horizontal layout with just a QTableView. You can create a QTableView object and place it inside a QHBoxLayout. Once the QWidget is properly built, pass the object to the QMainWindow as its central widget.
Remember that a QTableView needs a model to display information. In this case, you can use a QAbstractTableModel instance.
Note
You could also use the default item model that comes with a QTableWidget instead. QTableWidget is a convenience class that reduces your codebase considerably as you don’t need to implement a data model. However, it’s less flexible than a QTableView, as QTableWidget cannot be used with just any data. For more insight about Qt’s model-view framework, refer to the Model View Programming <https://doc.qt.io/qt-5/model-view-programming.html> documentation.
Implementing the model for your QTableView, allows you to: - set the headers, - manipulate the formats of the cell values (remember we have UTC time and float numbers), - set style properties like text alignment, - and even set color properties for the cell or its content.
To subclass the QAbstractTable, you must reimplement its virtual methods, rowCount(), columnCount(), and data(). This way, you can ensure that the data is handled properly. In addition, reimplement the headerData() method to provide the header information to the view.
Here is a script that implements the 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
Now, create a QWidget that has a QTableView, and connect it to your 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
You also need minor changes to the main_window.py
and
main.py
from chapter 3 to include the Widget inside the
MainWindow.
In the following snippets you’ll see those changes highlighted:
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