0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

Qt6 でハマった件

Posted at

はじめに

 PyQt6 を使っていて、少しハマったことがあったのでメモを残しておく。

 概要は以下。

  • ResizeToContents を設定している QHeaderView を使った QTableView で、アイテム数の少ないモデルに切り替えた際、Out of range を起こす。

 もしかしたら私の使い方がおかしいのかもしれない……。

確認環境

OS: Windows 11 Home (22H2)
Shell: Git Bash (git version 2.41.0.windows.3)
Python: 3.10.6
PyQt: 6.5.2

確認用コード

 少し長いかもしれない。(約120行)

test.py
# -*- coding: utf-8 -*-
import sys
from PyQt6 import QtCore, QtGui, QtWidgets


class _Model(QtCore.QAbstractTableModel):
    def __init__(self, items, headers, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self._items = items
        self._headers = headers

    def rowCount(self, *a, **k):
        return len(self._items)

    def columnCount(self, *a, **k):
        return len(self._headers)

    def flags(self, index):
        return super().flags(index)

    def data(self, index, role=QtCore.Qt.ItemDataRole.DisplayRole):
        if False:
            print('data', index.row(), index.column(),
                  role, self.roleNames().get(role, '<>'))

        if index.isValid():
            if role in (QtCore.Qt.ItemDataRole.DisplayRole,
                        QtCore.Qt.ItemDataRole.EditRole):
                return self._items[index.row()][index.column()]
        return None

    def headerData(self, section, orientation,
                   role=QtCore.Qt.ItemDataRole.DisplayRole):
        if orientation == QtCore.Qt.Orientation.Horizontal and \
           role == QtCore.Qt.ItemDataRole.DisplayRole:
            print('In headerData: ', section, 'of', len(self._headers))
            try:
                assert section < len(self._headers), \
                    'Not section(%d) < len(%d)' % (section, len(self._headers))
                return self._headers[section]
            except Exception as e:
                print(str(e))
                return '------'
        return super().headerData(section, orientation, role)


class _HHeader(QtWidgets.QHeaderView):
    def __init__(self, parent):
        super().__init__(QtCore.Qt.Orientation.Horizontal, parent)
        if True:
            self.setSectionResizeMode(
                QtWidgets.QHeaderView.ResizeMode.ResizeToContents)


class _Table(QtWidgets.QTableView):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.setHorizontalHeader(_HHeader(self))


class Main(QtWidgets.QMainWindow):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self._setupUI()

        self.m1 = _Model(items=[['a', 'b', 'c'], ['a', 'b', 'c']],
                         headers=['#1', '#2', '#3'])
        self.m2 = _Model(items=[],
                         headers=[])

    def _setupUI(self):
        self.setWindowTitle("test")
        self.resize(600, 400)

        menubar = self.menuBar()
        filemenu = menubar.addMenu("テスト(&T)")

        act = QtGui.QAction("Model #1", self)
        act.triggered.connect(self.setModel1)
        filemenu.addAction(act)

        act = QtGui.QAction("Model #2a", self)
        act.triggered.connect(self.setModel2a)
        filemenu.addAction(act)

        act = QtGui.QAction("Model #2b", self)
        act.triggered.connect(self.setModel2b)
        filemenu.addAction(act)

        act = QtGui.QAction("閉じる(&X) ", self)
        act.setShortcut("Ctrl+Q")
        act.setStatusTip("ウィンドウを閉じる")
        act.triggered.connect(self.close)
        filemenu.addAction(act)

        self.tv = _Table(self)
        self.setCentralWidget(self.tv)

    def setModel1(self):
        print('setModel1')
        self.tv.setModel(self.m1)

    def setModel2a(self):
        print('setModel2a')
        self.tv.setModel(self.m2)

    def setModel2b(self):
        print('setModel2b')
        self.tv.setModel(None)
        self.tv.setModel(self.m2)


if __name__ == '__main__':
    q = QtWidgets.QApplication(sys.argv)
    m = Main()
    m.show()
    q.exec()

動作確認

1. 上記 test.py を実行する。

test.pyの実行
$ python test.py

2. ウィンドウが立ち上がったら、メニューの [テスト] から [Model #1] を選ぶ。
  ⇒縦2,横3のテーブルが表示される。

3. メニューの [テスト] から [Model #2a]を選ぶ。
  ⇒エラーが発生。以下のようなメッセージが表示されるハズ。

実行ログ
setModel2a
In headerData:  0 of 0
Not section(0) < len(0)
In headerData:  1 of 0
Not section(1) < len(0)
In headerData:  2 of 0
Not section(2) < len(0)

 アイテムがあるモデルから、アイテムの無いモデルへ切り替えると、エラーが発生する様子。

 これを回避するには、いったんモデルを None にする必要があるみたい。

4. もう一度メニューの [テスト] から [Model #1] を選ぶ。
  ⇒縦2,横3のテーブルが表示される。

5. メニューの [テスト] から [Model #2b]を選ぶ。
  ⇒内部的には、いったん None を設定してから空のモデルに設定しているので、今度はエラーは発生しない……ハズ。

終わりに

 ちなみに、別の環境(PyQt6 6.2.3 on Ubuntu 22.04)でも同様の動作をする。なので、私の使い方が間違っているのかもしれない。なにか勘違いをしているのかも? もしくは、いったん None を入れるのが正しいお作法……なのかもしれない?

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?