2021-05-01

【備忘録】QTableView を使って表作成

Qt for Python (PySide6) は、Qt 6 の Python バインディングで、Qt 6と同じく、LGPLv3 / GPLv3Qt commercial license(商用ライセンス)が適用されています。

Qt の GUI を使ってデータ解析をする際、たくさんのデータを表示できる QTableView のようなウィジェットの活用は欠かせません。そう思うわりにはちゃんと使いこなせていないので、いろいろな用途のベースに使えるような QTableView のサンプルを作りました。

本ブログ記事では下記の OS 環境で動作確認をしています。

Fedora 34 Workstation x86_64
- python3-3.9.4-1
- venv/pip: PySide6 (6.0.3)

QTableView のサンプル

今回サンプルを作るにあたって、次の二点を最低限実装したい機能としました。データを表示することを第一の目的にしているので、表示したデータの編集をここでは考慮していません。

最低限実装したい機能
  • データは Excel のファイルを読み込む。
  • テーブルのセルでは、文字列であれば左寄せ、数値であれば右寄せする。

読み込む Excel ファイルは、総務省統計局のホームページから入手できる統計データ、日本の統計 第1章 国土・気象から「国土状況(エクセル:13KB)[1]」をダウンロードして、扱い易いように整形したサンプル(下記)を使っています。

データファイル sample.xlsx

QTableView の使い方については、いろいろなサイトの情報を参考にさせていただいていますが、そのなかでも参考サイト [2] で説明されている内容を大変参考にさせていただきました。

下記にサンプルを示しました。

qt_tableview_excel_1.py
#!/usr/bin/env python
# coding: utf-8
# Reference:
# https://pc-technique.info/2020/02/207/
import pandas as pd
import sys
from typing import Any
from PySide6.QtCore import (
Qt,
QModelIndex,
QAbstractTableModel
)
from PySide6.QtGui import QPainter
from PySide6.QtWidgets import (
QApplication,
QHeaderView,
QItemDelegate,
QMainWindow,
QStyleOptionViewItem,
QTableView,
)
class ExampleDelegate(QItemDelegate):
def paint(self, painter: QPainter, option: QStyleOptionViewItem, index: QModelIndex):
type_cell = type(index.data())
if type_cell is str:
option.displayAlignment = Qt.AlignLeft
elif type_cell is int:
option.displayAlignment = Qt.AlignRight
elif type_cell is float:
option.displayAlignment = Qt.AlignRight
else:
option.displayAlignment = Qt.AlignCenter
QItemDelegate.paint(self, painter, option, index)
class ExampleTableModel(QAbstractTableModel):
def __init__(self, df: pd.DataFrame):
super().__init__()
self.headers = df.columns.values
self.values = df.values.tolist()
def data(self, index: QModelIndex, role: int) -> Any:
if role == Qt.DisplayRole:
return self.values[index.row()][index.column()]
def rowCount(self, parent=QModelIndex()) -> int:
return len(self.values)
def columnCount(self, parent=QModelIndex()) -> int:
return len(self.headers)
def headerData(self, section: int, orientation: Qt.Orientation, role: int = Qt.DisplayRole) -> Any:
if role != Qt.DisplayRole:
return
if orientation == Qt.Horizontal:
return self.headers[section]
else:
return "{}".format(section + 1)
class Example(QMainWindow):
def __init__(self):
super().__init__()
# sample data
filename = 'sample.xlsx'
df = pd.read_excel(
filename,
engine='openpyxl',
)
self.initUI(df)
self.setWindowTitle('TableView')
self.resize(400, 300)
def initUI(self, df: pd.DataFrame):
table = QTableView()
table.setStyleSheet('QHeaderView::section {color:#004; background-color:#ddf}')
table.horizontalHeader().setSectionResizeMode(QHeaderView.ResizeToContents)
table.verticalHeader().setSectionResizeMode(QHeaderView.ResizeToContents)
table.setWordWrap(False)
# table model
table.setModel(ExampleTableModel(df))
# table item delegate
table.setItemDelegate(ExampleDelegate())
self.setCentralWidget(table)
def main():
app = QApplication(sys.argv)
ex = Example()
ex.show()
sys.exit(app.exec())
if __name__ == "__main__":
main()

実行例を下記に示しました。

qt_tableview_excel_1.py の実行例

改善したい項目

ちょっとデータテーブルを表示したい時に、今回のサンプルをベースにすれば簡単にできるようになりましたが、まだまだ改善したい項目はあります。以下が改善したい主な項目です。

改善あるいは実装したい機能
  • 数値の小数点以下の桁数を揃える。
    • フォーマットしてしまえば良いのですが、そうすると文字列として扱わなければならないので、どのようにするのが汎用性を維持するのに都合が良いかを思案中です。
  • テーブルセル内のマージンなどを自在にコントロールできるようにしたい。
    • スタイルシートを使って設定しようとしていますが、自在に、というレベルにはほど遠い状態です。

参考サイト

  1. 統計局ホームページ/日本の統計 2021-第1章 国土・気象
  2. 【PySide2】QTableViewとModelの作り方 - その1 | WhiteAtelier Archives [2020-02-27]

ブログランキング・にほんブログ村へ bitWalk's - にほんブログ村 にほんブログ村 IT技術ブログ Linuxへ
にほんブログ村

2 件のコメント:

あいう さんのコメント...

ソースがみれませぬ。

bitWalk さんのコメント...

すみません、見えるように修正しました。