PyQt学习——day5

模型视图机制

优势:相较于基于widget的模型,model/view的在最大好处在于共享data,一份data,可以被多个view使用,实现信息同步更新;而如果想利用widget做到这点,就必需手动去设置复杂的信号槽关系。
劣势:相关的概念较多,上手比较困难。

自定义listmodel

  1. 继承QAbstractListModel
  2. 结构:rowCount()
  3. 显示:data()
  4. 编辑:flags()、setData()
  5. 增减数据:insertRows()、removeRows()
# 自定义自己的listmodel
import typing
from PyQt5.QtCore import Qt, QAbstractListModel, QModelIndex
from PyQt5.QtGui import QIcon
from PyQt5.QtWidgets import QApplication, QListView, QComboBox


class MyListModel(QAbstractListModel):
    def __init__(self, names=[], parent=None):
        super(MyListModel, self).__init__(parent)

        self.__name = names
        self.__icon = QIcon('0.png')

    def rowCount(self, parent) -> int:
        return len(self.__name)

    def data(self, index: QModelIndex, role: int = ...) -> typing.Any:
        row = index.row()
        if role == Qt.DisplayRole:
            return self.__name[row]

        if role == Qt.EditRole:
            return self.__name[row]

        if role == Qt.DecorationRole:
            return self.__icon

    def flags(self, index: QModelIndex) -> Qt.ItemFlags:
        return Qt.ItemIsEditable | Qt.ItemIsEnabled | Qt.ItemIsSelectable

    def headerData(self, section: int, orientation: Qt.Orientation, role: int = ...) -> typing.Any:
        return "未定义"

    def setData(self, index: QModelIndex, value: typing.Any, role: int = ...) -> bool:
        row = index.row()
        if role == Qt.EditRole:
            self.__name[row] = value
            self.dataChanged.emit(index, index)  # 注意这里需要发出一个通知
            return True

        return False

    def insertRows(self, row: int, count: int, parent=QModelIndex()) -> bool:
        self.beginInsertRows(QModelIndex(), row, row + count - 1)
        for i in range(count):
            self.__name.insert(row, '默认')
        self.endInsertRows()

    def removeRows(self, row: int, count: int, parent=QModelIndex()) -> bool:
        self.beginRemoveRows(QModelIndex(), row, row + count - 1)
        for i in range(count):
            self.__name.pop(row)
        self.endRemoveRows()

if __name__ == '__main__':
    app = QApplication([])
    model = MyListModel(['小红', '小小', '星星', '大哥', '憨憨'])

    w = QListView()
    w.setModel(model)
    w.show()

    box = QComboBox()
    box.setModel(model)
    box.show()

    model.insertRows(0, 2)
    model.removeRows(0, 2)

    app.exec_()

自定义tableModel

  1. 继承QAbstractTableModel
  2. 结构:rowCount()、colCount()
  3. 显示:data()、headerData()
  4. 编辑:flags()、setData()
  5. 增减数据:insertRows()、removeRows()、insertColumns()、removeColums()
# myTableModel
import typing
from PyQt5.QtCore import Qt, QAbstractTableModel, QModelIndex
from PyQt5.QtGui import QIcon
from PyQt5.QtWidgets import QApplication, QListView, QComboBox, QTableView


class MyTableModel(QAbstractTableModel):
    def __init__(self, data=[[]], parent=None):
        super(MyTableModel, self).__init__(parent)

        self.__data = data
        self.__headers = ['名字', '年龄']

    def rowCount(self, parent) -> int:
        return len(self.__data)

    def columnCount(self, parent: QModelIndex = ...) -> int:
        return len(self.__data[0])

    def flags(self, index: QModelIndex) -> Qt.ItemFlags:
        return Qt.ItemIsEditable | Qt.ItemIsEnabled | Qt.ItemIsSelectable

    def data(self, index: QModelIndex, role: int = ...) -> typing.Any:
        row, col = index.row(), index.column()

        if role == Qt.DisplayRole:
            return self.__data[row][col]

        if role == Qt.EditRole:
            return self.__data[row][col]

    def headerData(self, section: int, orientation: Qt.Orientation, role: int = ...):
        if orientation == Qt.Horizontal:
            if role == Qt.DisplayRole:
                if section < len(self.__headers):
                    return self.__headers[section]
                else:
                    return 'undefined'

    def setData(self, index: QModelIndex, value: typing.Any, role: int = ...) -> bool:
        row, col = index.row(), index.column()

        if role == Qt.EditRole:
            self.__data[row][col] = value
            self.dataChanged.emit(index, index)  # 注意这里需要发出一个通知
            return True

        return False

    def insertRows(self, row: int, count: int, parent=QModelIndex()) -> bool:
        self.beginInsertRows(QModelIndex(), row, row + count - 1)

        for i in range(count):
            self.__data.insert(row, ['' for i in range(self.columnCount())])

        self.endInsertRows()
        return True

    def removeRows(self, row: int, count: int, parent=QModelIndex()) -> bool:
        self.beginRemoveRows(QModelIndex(), row, row + count - 1)

        for i in range(count):
            self.__data.pop(row)

        self.endRemoveRows()
        return True


if __name__ == '__main__':
    app = QApplication([])
    model = MyTableModel([['小红', 19]])

    tableView = QTableView()
    tableView.setModel(model)
    tableView.show()

    model.insertRows(0, 2)
    model.removeRows(0, 2)

    app.exec_()

自定义treeModel

  1. 自定义树结构,维护好:parent、children、data
  2. 结构:rowCount()、columnCount()、index()
  3. 数据显示:headerData()、data()
  4. 编辑:flags()、setData()
  5. 增删:insertRow()、removeRow()
  6. 父节点:parent()

使用已有模型

QFileSystemModel使用

# 利用已有的模型做使用展示
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
import os

class Demo(QWidget):
    def __init__(self):
        super(Demo, self).__init__()

        self.__setttingWindow()
        self.__buildUI()

    # ================== 功能函数 ==================
    def __setttingWindow(self):
        self.resize(640, 480)

    def __buildUI(self):
        vbox = QVBoxLayout(self)
        splitter = QSplitter()
        vbox.addWidget(splitter)

        # 模型
        model = QFileSystemModel()
        model.setRootPath('')  # 文档上说设置监视范围,没弄清楚是什么意思;如果不设置不会显示;

        # 树形视图
        treeView = QTreeView(splitter)
        treeView.setModel(model)
        treeView.setRootIndex(model.index('c:')) # 设置可见范围

        # 列表型视图
        listView = QListView(splitter)
        listView.setModel(model)
        listView.setRootIndex(model.index(os.getcwd())) # 设置可见范围, 只有直接子节点会显示


if __name__ == '__main__':
    app = QApplication([])
    w = Demo()
    w.show()
    app.exec_()

代理模型

model <----> proxy <----> view
通过在模型上在加一个代理模型,可以实现很多其他的功能:排序、过滤

from PyQt5.QtCore import Qt, QSortFilterProxyModel
from PyQt5.QtGui import QStandardItemModel, QStandardItem
from PyQt5.QtWidgets import QApplication, QListView, QWidget, QLineEdit, QTableView, QVBoxLayout
import random


class Demo(QWidget):
    def __init__(self):
        super(Demo, self).__init__()
        self.__buildUI()

    def __buildUI(self):
        vbox = QVBoxLayout(self)
        # 输入re框
        self.editRE = QLineEdit()
        vbox.addWidget(self.editRE)

        # 模型
        model = QStandardItemModel()
        for i in range(10):
            model.insertRow(0, QStandardItem(f'{random.randint(0, 20)}'))

        # 代理
        proxyModel = QSortFilterProxyModel()
        proxyModel.setSourceModel(model)
        self.editRE.textChanged.connect(proxyModel.setFilterRegExp)
        proxyModel.setDynamicSortFilter(True)

        # 视图
        self.view = QListView()
        self.view.setModel(proxyModel)
        # self.view.setSortingEnabled(True)
        vbox.addWidget(self.view)


if __name__ == '__main__':
    app = QApplication([])
    w = Demo()
    w.show()
    app.exec_()

使用QStandarItemModel 搭配各类ViewQListView、QWidgetView、QTreeView

# 简单使用
from PyQt5.QtCore import QModelIndex, Qt
from PyQt5.QtWidgets import QApplication, QListView, QTableView, QTreeView
from PyQt5.QtGui import QStandardItemModel


if __name__ == '__main__':
    app = QApplication([])

    # list只需要一个列
    listModel = QStandardItemModel(5, 1)
    animals = ['小猫', '小狗', '小猪', '兔子', '猫头鹰']
    for i in range(listModel.rowCount()):
        index = listModel.index(i, 0)
        listModel.setData(index, animals[i])

    listView = QListView()
    listView.setModel(listModel)
    # listView.show()

    # table多了一步设计表头
    tableModel = QStandardItemModel(5, 2)
    tableModel.setHorizontalHeaderLabels(['名称', '年龄'])
    ages = [3, 2, 1, 3, 4]
    for i in range(tableModel.rowCount()):
        index = tableModel.index(i, 0)
        tableModel.setData(index, animals[i])

        index = tableModel.index(i, 1)
        tableModel.setData(index, ages[i])

    tableView = QTableView()
    tableView.setModel(tableModel)
    # tableView.show()

    # tree
    treeModel = QStandardItemModel(2, 2)
    treeModel.setHorizontalHeaderLabels(['名称', '年龄'])
    type = ['动物', '植物']
    for i in range(2):
        index = treeModel.index(i, 0)
        treeModel.setData(index, type[i])

    animalParentIndex = treeModel.index(0, 0)
    treeModel.insertRows(0, 5, animalParentIndex)
    treeModel.insertColumns(0, 2, animalParentIndex)
    for i in range(5):
        index = treeModel.index(i, 0, animalParentIndex)
        treeModel.setData(index, animals[i])
        index = treeModel.index(i, 1, animalParentIndex)
        treeModel.setData(index, ages[i])

    treeView = QTreeView()
    treeView.setModel(treeModel)
    treeView.show()

    app.exec_()

代表

就是一个获取输入、显示数据的组件,本质上就是一个Qwidget,只不过其显示的内容和model里面的值绑定上了。我们不单单可以使用默认的组件,也可以自己实现组件,并将其作为delegate。

  1. 继承QStyleItemDelegate
  2. 创建:createEditor()
  3. 设置编辑器:setEditorData()
  4. 设置模型:setModelData()
  5. 设置位置:updateEditorGeometry()
from PyQt5.QtWidgets import QApplication, QTableView, QStyledItemDelegate, QWidget, QLineEdit, QSpinBox
from PyQt5.QtGui import QStandardItemModel, QStandardItem, QIcon
from PyQt5.QtCore import Qt, QModelIndex, QAbstractItemModel


class MyDelegate(QStyledItemDelegate):
    def createEditor(self, parent: QWidget, option: 'QStyleOptionViewItem', index: QModelIndex) -> QWidget:
        if index.column() == 0:
            return QLineEdit(parent)
        else:
            return QSpinBox(parent)

    def setEditorData(self, editor: QWidget, index: QModelIndex) -> None:
        if index.column() == 0:
            editor.setText(index.data(Qt.EditRole))
        else:
            editor.setValue(index.data(Qt.EditRole))

    def setModelData(self, editor: QWidget, model: QAbstractItemModel, index: QModelIndex) -> None:
        if index.column() == 0:
            model.setData(index, editor.text(), Qt.EditRole)
        else:
            model.setData(index, editor.value(), Qt.EditRole)

    def updateEditorGeometry(self, editor: QWidget, option: 'QStyleOptionViewItem', index: QModelIndex) -> None:
        editor.setGeometry(option.rect)


if __name__ == '__main__':
    app = QApplication([])
    model = QStandardItemModel(2, 2)
    # 初始化
    icon = QIcon('0.png')

    for i in range(2):
        index = model.index(i, 0)
        model.setData(index, f'小{i}')
        index = model.index(i, 1)
        model.setData(index, 10)

    model.setHorizontalHeaderLabels(['姓名', '年龄'])
    view = QTableView()
    view.setModel(model)
    view.setItemDelegate(MyDelegate())
    view.show()
    app.exec_()
# 自己实现的简单滑动编辑:)
from PyQt5.QtWidgets import QApplication, QWidget, QListView, QStyledItemDelegate
from PyQt5.QtGui import QPainter, QPaintEvent, QMouseEvent, QStandardItemModel
from PyQt5.QtCore import Qt, QSize, pyqtSignal, QModelIndex, QAbstractItemModel


class PowerLine(QWidget):
    editingFinished = pyqtSignal()

    def __init__(self, parent=None, curPower=20, maxPower=100):
        super(PowerLine, self).__init__(parent)

        self.curPower = curPower
        self.maxPower = maxPower
        self.isSetting = False

        self.__setting()

    # ================== 辅助函数 ==================
    def __setting(self):
        self.resize(408, 100)
        self.setMouseTracking(True)
        self.setAutoFillBackground(True)  # 当在老画面上绘制时,自动清空

    def __getPower(self, pos):
        miniScale = self.width() / 108
        power = (pos.x() - 2 * miniScale) / miniScale
        return int(min(100, max(0, power)))

    # ================== 事件 ==================
    def paintEvent(self, event: QPaintEvent) -> None:
        painter = QPainter(self)
        # 视图、窗口
        painter.setViewport(self.rect())
        w, h = self.width(), self.height()
        painter.setWindow(0, 0, 108, 66)

        # 外边框
        pen = painter.pen()
        pen.setWidth(2)
        painter.setPen(pen)
        painter.drawRect(2, 2, 104, 60)

        # 内部
        pen = painter.pen()
        pen.setColor(Qt.green)
        pen.setWidth(0)
        painter.setPen(pen)

        brush = painter.brush()
        brush.setColor(Qt.green)
        brush.setStyle(Qt.SolidPattern)
        painter.setBrush(brush)

        if self.curPower > 0:
            painter.drawRect(4, 5, self.curPower, 54)

    def mousePressEvent(self, event: QMouseEvent) -> None:
        self.isSetting = True

        self.curPower = self.__getPower(event.pos())
        self.update()

    def mouseMoveEvent(self, event: QMouseEvent) -> None:
        if not self.isSetting:
            return

        power = self.__getPower(event.pos())

        if self.curPower != int(power):
            self.curPower = int(power)
            self.update()

    def mouseReleaseEvent(self, event: QMouseEvent) -> None:
        self.isSetting = False
        self.editingFinished.emit()

    # ================== 功能函数 ==================
    def sizeHint(self) -> QSize:
        return QSize(104, 50)


class MyDelegate(QStyledItemDelegate):
    def createEditor(self, parent: QWidget, option: 'QStyleOptionViewItem', index: QModelIndex) -> QWidget:
        powerline = PowerLine(parent)
        powerline.editingFinished.connect(self.commitAndCloseEditor)
        return powerline

    def setEditorData(self, editor: QWidget, index: QModelIndex) -> None:
        val = index.data()
        editor.curPower = val

    def setModelData(self, editor: QWidget, model: QAbstractItemModel, index: QModelIndex) -> None:
        val = editor.curPower
        model.setData(index, val, Qt.DisplayRole)

    def commitAndCloseEditor(self):
        editor = self.sender()
        self.commitData.emit(editor)
        self.closeEditor.emit(editor)


if __name__ == '__main__':
    app = QApplication([])

    model = QStandardItemModel(2, 1)
    index = model.index(0, 0, QModelIndex())
    model.setData(index, 10, Qt.DisplayRole)
    index = model.index(1, 0, QModelIndex())
    model.setData(index, 20, Qt.DisplayRole)

    listView = QListView()
    listView.setModel(model)
    listView.setItemDelegate(MyDelegate())
    listView.show()

    app.exec_()

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值