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 <http://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:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
from PySide2.QtCore import Qt, QAbstractTableModel, QModelIndex
from PySide2.QtGui import QColor
class CustomTableModel(QAbstractTableModel):
def __init__(self, data=None):
QAbstractTableModel.__init__(self)
self.load_data(data)
def load_data(self, data):
self.input_dates = data[0].values
self.input_magnitudes = data[1].values
self.column_count = 2
self.row_count = len(self.input_magnitudes)
def rowCount(self, parent=QModelIndex()):
return self.row_count
def columnCount(self, parent=QModelIndex()):
return self.column_count
def headerData(self, section, orientation, role):
if role != Qt.DisplayRole:
return None
if orientation == Qt.Horizontal:
return ("Date", "Magnitude")[section]
else:
return "{}".format(section)
def data(self, index, role=Qt.DisplayRole):
column = index.column()
row = index.row()
if role == Qt.DisplayRole:
if column == 0:
raw_date = self.input_dates[row]
date = "{}".format(raw_date.toPython())
return date[:-3]
elif column == 1:
return "{:.2f}".format(self.input_magnitudes[row])
elif role == Qt.BackgroundRole:
return QColor(Qt.white)
elif role == Qt.TextAlignmentRole:
return Qt.AlignRight
return None
|
Now, create a QWidget that has a QTableView, and connect it to your CustomTableModel.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
from PySide2.QtWidgets import (QHBoxLayout, QHeaderView, QSizePolicy,
QTableView, QWidget)
from table_model import CustomTableModel
class Widget(QWidget):
def __init__(self, data):
QWidget.__init__(self)
# Getting the Model
self.model = CustomTableModel(data)
# Creating a QTableView
self.table_view = QTableView()
self.table_view.setModel(self.model)
# QTableView Headers
self.horizontal_header = self.table_view.horizontalHeader()
self.vertical_header = self.table_view.verticalHeader()
self.horizontal_header.setSectionResizeMode(
QHeaderView.ResizeToContents
)
self.vertical_header.setSectionResizeMode(
QHeaderView.ResizeToContents
)
self.horizontal_header.setStretchLastSection(True)
# QWidget Layout
self.main_layout = QHBoxLayout()
size = QSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred)
## Left layout
size.setHorizontalStretch(1)
self.table_view.setSizePolicy(size)
self.main_layout.addWidget(self.table_view)
# Set the layout to the QWidget
self.setLayout(self.main_layout)
|
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:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 | #############################################################################
##
## Copyright (C) 2019 The Qt Company Ltd.
## Contact: http://www.qt.io/licensing/
##
## This file is part of the Qt for Python examples of the Qt Toolkit.
##
## $QT_BEGIN_LICENSE:BSD$
## You may use this file under the terms of the BSD license as follows:
##
## "Redistribution and use in source and binary forms, with or without
## modification, are permitted provided that the following conditions are
## met:
## * Redistributions of source code must retain the above copyright
## notice, this list of conditions and the following disclaimer.
## * Redistributions in binary form must reproduce the above copyright
## notice, this list of conditions and the following disclaimer in
## the documentation and/or other materials provided with the
## distribution.
## * Neither the name of The Qt Company Ltd nor the names of its
## contributors may be used to endorse or promote products derived
## from this software without specific prior written permission.
##
##
## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
## "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
## LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
## A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
## OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
## SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
## LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
## DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
## THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
##
## $QT_END_LICENSE$
##
#############################################################################
from PySide2.QtCore import Slot, qApp
from PySide2.QtGui import QKeySequence
from PySide2.QtWidgets import QMainWindow, QAction
class MainWindow(QMainWindow):
def __init__(self, widget):
QMainWindow.__init__(self)
self.setWindowTitle("Eartquakes information")
self.setCentralWidget(widget)
# Menu
self.menu = self.menuBar()
self.file_menu = self.menu.addMenu("File")
## Exit QAction
exit_action = QAction("Exit", self)
exit_action.setShortcut(QKeySequence.Quit)
exit_action.triggered.connect(self.close)
self.file_menu.addAction(exit_action)
# Status Bar
self.status = self.statusBar()
self.status.showMessage("Data loaded and plotted")
# Window dimensions
geometry = qApp.desktop().availableGeometry(self)
self.setFixedSize(geometry.width() * 0.8, geometry.height() * 0.7)
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 | #############################################################################
##
## Copyright (C) 2019 The Qt Company Ltd.
## Contact: http://www.qt.io/licensing/
##
## This file is part of the Qt for Python examples of the Qt Toolkit.
##
## $QT_BEGIN_LICENSE:BSD$
## You may use this file under the terms of the BSD license as follows:
##
## "Redistribution and use in source and binary forms, with or without
## modification, are permitted provided that the following conditions are
## met:
## * Redistributions of source code must retain the above copyright
## notice, this list of conditions and the following disclaimer.
## * Redistributions in binary form must reproduce the above copyright
## notice, this list of conditions and the following disclaimer in
## the documentation and/or other materials provided with the
## distribution.
## * Neither the name of The Qt Company Ltd nor the names of its
## contributors may be used to endorse or promote products derived
## from this software without specific prior written permission.
##
##
## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
## "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
## LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
## A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
## OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
## SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
## LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
## DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
## THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
##
## $QT_END_LICENSE$
##
#############################################################################
import sys
import argparse
import pandas as pd
from PySide2.QtCore import QDateTime, QTimeZone
from PySide2.QtWidgets import QApplication
from main_window import MainWindow
from main_widget import Widget
def transform_date(utc, timezone=None):
utc_fmt = "yyyy-MM-ddTHH:mm:ss.zzzZ"
new_date = QDateTime().fromString(utc, utc_fmt)
if timezone:
new_date.setTimeZone(timezone)
return new_date
def read_data(fname):
# Read the CSV content
df = pd.read_csv(fname)
# Remove wrong magnitudes
df = df.drop(df[df.mag < 0].index)
magnitudes = df["mag"]
# My local timezone
timezone = QTimeZone(b"Europe/Berlin")
# Get timestamp transformed to our timezone
times = df["time"].apply(lambda x: transform_date(x, timezone))
return times, magnitudes
if __name__ == "__main__":
options = argparse.ArgumentParser()
options.add_argument("-f", "--file", type=str, required=True)
args = options.parse_args()
data = read_data(args.file)
# Qt Application
app = QApplication(sys.argv)
widget = Widget(data)
window = MainWindow(widget)
window.show()
sys.exit(app.exec_())
|