PyQt5 QTableViewのサンプル

PyQt5でQTableViewのサンプルを作ってみました。



機能

  • QTableViewの表示、編集
  • 行の挿入
  • 行の削除
  • コンテキストメニュー

列数の設定

self.model.column = 100

表の列数は上記のコードで設定していて、行の挿入を行ったときにself.model.columnの要素数を持ったリストを行に追加します。

行の挿入

def insertRow(self):
    indexes = self.ui.tableView.selectedIndexes()
    
    if len(indexes) == 0:
        item = [ str(self.model.rowCount()) + str(i) for i in range(self.model.column) ]
        self.model.addItem( self.model.rowCount(), item )
        return
    
    indexes2 = []
    for index in indexes[::-1]:
        if not index.row() in [ index2.row() for index2 in indexes2 ]:
            indexes2.append(index)

    for index in indexes2[::-1]:
        item = [ str(self.model.rowCount()) + str(i) for i in range(self.model.column) ]
        self.model.addItem( index.row() + 1, item )

処理内容は下記の通りです。

  • 選択しているセルのインデックス(QModelIndex)を取得
  • 選択してるインデックスが0個=何も選択されていなければ最後の行に追加
  • 選択されているセルがあれば、行の重複がないインデックスのリストを作成して、そのインデックスの次の行に行を挿入

行の削除

def delItem(self):
    indexes = self.ui.tableView.selectedIndexes()
    
    if self.model.rowCount() == 0:
        return

    if len(indexes) == 0:
        self.model.removeItem( self.model.rowCount()-1 )
        return
    
    rows = set( [ index.row() for index in indexes ] )
    for row in list(rows)[::-1]:
        self.model.removeItem( row )

処理内容は下記の通りです。

  • 表に行がなければ処理終了
  • 選択しているセルがなければ最後の行を削除
  • 選択しているセルがあれば、選択しているセルから重複無しの行番号のリストを作成して、そのリストの最後から行を削除

コンテキストメニュー

def contextMenu(self, point):
    self.menu = QtWidgets.QMenu(self)
    self.menu.addAction('Insert', self.insertRow)
    self.menu.addAction('Delete', self.delItem)
    self.menu.exec_( self.focusWidget().mapToGlobal(point) )

コンテキストメニューには前述の行の追加、削除の機能を設定しています。



ソースコード

作成したコードはgithubにあります。

main.py

import sys
from mainwindow import Ui_MainWindow
from tableview import Model, Delegate, Item
from PyQt5 import QtWidgets, QtCore

class MainWindow(QtWidgets.QMainWindow):
    def __init__(self):
        super().__init__()

        self.ui = Ui_MainWindow()
        self.ui.setupUi(self)

        self.model = Model(self)
        self.model.column = 100

        self.ui.tableView.setModel(self.model)
        self.ui.tableView.setItemDelegate(Delegate())
        self.ui.tableView.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
        self.ui.tableView.customContextMenuRequested.connect(self.contextMenu)
        self.ui.pushButton.clicked.connect(self.insertRow)
        self.ui.pushButton_2.clicked.connect(self.delItem)

    def contextMenu(self, point):
        self.menu = QtWidgets.QMenu(self)
        self.menu.addAction('Insert', self.insertRow)
        self.menu.addAction('Delete', self.delItem)
        self.menu.exec_( self.focusWidget().mapToGlobal(point) )
 
    def insertRow(self):
        indexes = self.ui.tableView.selectedIndexes()
        
        if len(indexes) == 0:
            item = [ str(self.model.rowCount()) + str(i) for i in range(self.model.column) ]
            self.model.addItem( self.model.rowCount(), item )
            return
        
        indexes2 = []
        for index in indexes[::-1]:
            if not index.row() in [ index2.row() for index2 in indexes2 ]:
                indexes2.append(index)

        for index in indexes2:
            item = [ str(self.model.rowCount()) + str(i) for i in range(self.model.column) ]
            self.model.addItem( index.row() + 1, item )

    def delItem(self):
        indexes = self.ui.tableView.selectedIndexes()
        
        if self.model.rowCount() == 0:
            return

        if len(indexes) == 0:
            self.model.removeItem( self.model.rowCount()-1 )
            return
        
        rows = set( [ index.row() for index in indexes ] )
        for row in list(rows)[::-1]:
            self.model.removeItem( row )
 
def main():
    app = QtWidgets.QApplication(sys.argv)
    window = MainWindow()
    window.show()
    app.exec_()
 
if __name__ == '__main__':
    main()

tableview.py

# -*- coding: utf-8 -*-
from PyQt5 import QtWidgets, QtCore
 
class Item(object):
    def __init__(self, _parent=None):
        self._dict = {}
        self.parent_item = _parent
        self.children = []
    
    def appendChild(self, item):
        self.children.append(item)

    def data(self, column):
        if column in self._dict.keys():
            return self._dict[column]
        return ''
 
    def setData(self, column, data):
        self._dict[column] = data
    
    def child(self, row):
        return self.children[row]

    def childrenCount(self):
        return len(self.children)

    def parent(self):
        return self.parent_item

    def removeChild(self, row):
        del self.children[row]

    def row(self):
        if self.parent_item:
            return self.parent_item.children.index(self)
        return 0

class Model(QtCore.QAbstractItemModel):
    def __init__(self, parent_=None):
        super(Model, self).__init__(parent_)
        self.items = []
        self.column = 0

    def addItem(self, row, item, parent=QtCore.QModelIndex()):
        self.beginInsertRows(parent, row, row)
        self.items.insert(row, item)
        self.endInsertRows()

    def columnCount(self, parent=QtCore.QModelIndex()):
        if len(self.items) == 0:
            return self.column
        return len(self.items[0])
 
    def data(self, index, role):
        if role == QtCore.Qt.EditRole or role == QtCore.Qt.DisplayRole:
            return self.items[index.row()][index.column()]
        return QtCore.QVariant()
        
    def flags(self, index):
        return QtCore.Qt.ItemIsEditable | QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsSelectable

    def headerData(self, i, orientation, role):
        if orientation == QtCore.Qt.Horizontal and role == QtCore.Qt.DisplayRole:
            return i
        if orientation == QtCore.Qt.Vertical and role == QtCore.Qt.DisplayRole:
            return i

    def index(self, row, column, parent=QtCore.QModelIndex()):
        return self.createIndex(row, column, parent)

    def parent(self, index):
        return QtCore.QModelIndex()
        
    def removeItem(self, row, parent=QtCore.QModelIndex()):
        self.beginRemoveRows(parent, row, row)
        del self.items[row]
        self.endRemoveRows()
 
    def rowCount(self, parent=QtCore.QModelIndex()):
        return len(self.items)

    def setData(self, index, value, role=QtCore.Qt.EditRole):
        if role == QtCore.Qt.EditRole:
            self.items[index.row()][index.column()] = value
            #index.internalPointer() = value
            return True
        return False

class Delegate(QtWidgets.QStyledItemDelegate):
    def __init__(self, parent=None, setModelDataEvent=None):
        super(Delegate, self).__init__(parent)
        self.setModelDataEvent = setModelDataEvent
 
    def createEditor(self, parent, option, index):
        return QtWidgets.QLineEdit(parent)
 
    def setEditorData(self, editor, index):
        value = index.model().data(index, QtCore.Qt.DisplayRole)
        editor.setText(str(value))
 
    def setModelData(self, editor, model, index):
        model.setData(index, editor.text())
        if not self.setModelDataEvent is None:
            self.setModelDataEvent()

コメント

タイトルとURLをコピーしました