PyQt5表格行拖拽排序

使用PyQt5自带的表格行拖拽,虽然好看,但总是有些小问题。自己动手搞一个,记录一下,方便下次使用。

# _*_ coding:utf-8 _*_
# @File  : 拖拽表格.py
# @Time  : 2021-05-10 15:42
# @Author: zizle
import random
import sys

from PyQt5.QtCore import Qt
from PyQt5.QtGui import QIcon, QBrush, QColor, QPalette
from PyQt5.QtWidgets import QApplication, QTableWidget, QTableWidgetItem, QAbstractItemView, QPushButton, QHeaderView, \
    QListWidget, QWidget, QHBoxLayout, QLabel, QFrame


class TableWidget(QTableWidget):

    # class Label(QWidget,QPainter):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.resize(800, 400)

        self.setDragEnabled(True)
        self.setAcceptDrops(True)
        self.viewport().setAcceptDrops(True)
        self.setDragDropOverwriteMode(False)
        self.setDropIndicatorShown(True)

        self.setSelectionMode(QAbstractItemView.ExtendedSelection)
        self.setSelectionBehavior(QAbstractItemView.SelectRows)
        self.setDragDropMode(QAbstractItemView.InternalMove)

        self.drag_data = dict()  # 记录被拖拽出行的数据
        self.drag_row = -2  # 记录被拖拽的行
        self.drop_row = -2  # 记录被放下的行
        self.is_dragging = False

    def mouseReleaseEvent(self, event) -> None:
        print('songkai')
        if self.is_leave:
            self.is_dragging = False

    def dragEnterEvent(self, event) -> None:  # 开始拖动
        if self.is_dragging:
            return
        row, col = self.indexAt(event.pos()).row(),  self.indexAt(event.pos()).column()
        drag_item = self.item(row, 0)
        if col != 0 or not drag_item:
            return
        self.drag_data = drag_item.data(Qt.UserRole)
        if not self.drag_data:
            return
        # 记录拖动行
        self.drag_row = row
        super(TableWidget, self).dragEnterEvent(event)  # 拖动
        self.is_dragging = True
        self.removeRow(self.drag_row)  # 删除行

    def dragMoveEvent(self, event) -> None:  # 在控件内部拖动
        row, col = self.indexAt(event.pos()).row(), self.indexAt(event.pos()).column()
        print('目标row', row, col)
        if row < 0 or col < 0:  # 移除表格有数据的区域
            # 放回原位置还原状态
            self.reset_old_row()
            return
        super(TableWidget, self).dragMoveEvent(event)
        # 还原上下行颜色
        self.set_row_bg_color(row - 1, QColor(255, 255, 255))
        self.set_row_bg_color(row + 1, QColor(255, 255, 255))
        # 设置当前行高亮
        self.set_row_bg_color(row, QColor(254, 163, 86))
        self.drop_row = row  # 目标行

    def set_row_bg_color(self, row, color):
        if row < 0:
            return
        for col in range(self.columnCount()):
            item = self.item(row, col)
            if item:
                item.setBackground(QBrush(color))
                
    def dragLeaveEvent(self, event) -> None:
        print('拖出行:', self.drag_row)
        print('目标行:', self.drop_row)
        print(help(event))
        # 放回数据,清空记录数据
        self.reset_old_row()

    def reset_old_row(self):
        self.insert_row_data(self.drag_row, self.drag_data)
        self.set_row_bg_color(self.drop_row + 1, QColor(255, 255, 255))
        self.reset_drag_items()

    def reset_drag_items(self):
        self.drag_data.clear()
        self.drag_row = -2
        self.drop_row = -2
        self.is_dragging = False

    def dropEvent(self, event) -> None:
        if self.drop_row < 0:
            return
        self.insert_row_data(self.drop_row, self.drag_data)
        self.set_row_bg_color(self.drop_row + 1, QColor(255, 255, 255))
        print('拖出行:', self.drag_row)
        print('目标行:', self.drop_row)
        self.selectRow(self.drop_row)
        self.reset_drag_items()

    def get_table_data(self):
        data = []
        s = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
        for i in range(2101, 2120):
            data.append({'id': i, 'name': s[i - 2100], 'value': random.randint(1, 200)})
        return data

    def set_data(self):
        self.setColumnCount(4)
        self.horizontalHeader().setSectionResizeMode(3, QHeaderView.ResizeToContents)
        for row, row_item in enumerate(self.get_table_data()):
            self.insert_row_data(row, row_item)

    def insert_row_data(self, row, row_data):
        if not row_data:
            return
        self.insertRow(row)
        for col, key in enumerate(['id', 'name', 'value', '']):
            if col == 3:
                item = QTableWidgetItem()
                item.setIcon(QIcon('media/icons/delete.png'))
            else:
                item = QTableWidgetItem(str(row_data[key]))
            if col == 0:
                item.setData(Qt.UserRole, row_data)
            self.setItem(row, col, item)


class DraggableTableWidget(QTableWidget):
    def __init__(self, *args, **kwargs):
        super(DraggableTableWidget, self).__init__(*args, **kwargs)
        self.resize(400, 450)
        self.setSelectionBehavior(QAbstractItemView.SelectRows)

        self.data_keys = ['id', 'name', 'value']

        self.drag_row = -1
        self.drop_row = None

        self.drag_widget = None
        self.init_drag_widget()

    def set_drag_data_on_widget(self, row_data):
        drag_layout = QHBoxLayout(self.drag_widget)
        drag_layout.setContentsMargins(10, 0, 0, 0)
        for col, key in enumerate(self.data_keys):
            text_label = QLabel(str(row_data[key]), self.drag_widget)
            text_label.setFixedWidth(self.columnWidth(col))
            text_label.setAlignment(Qt.AlignCenter)
            drag_layout.addWidget(text_label)
            button = QPushButton(self)
            button.setFocusPolicy(Qt.NoFocus)
            button.setFixedWidth(1)
            drag_layout.addWidget(button)
        drag_layout.addStretch()
        self.drag_widget.setLayout(drag_layout)
        setattr(self.drag_widget, 'row_data', row_data)

    def init_drag_widget(self):
        if self.drag_widget is not None and isinstance(self.drag_widget, QWidget):
            self.drag_widget.deleteLater()
            self.drag_widget = None
        self.drag_widget = QWidget(self)
        p = self.drag_widget.palette()
        p.setColor(QPalette.Background, QColor(0, 200, 100))
        self.drag_widget.setPalette(p)
        self.drag_widget.setAutoFillBackground(True)

        self.drag_widget.resize(self.width(), 30)
        self.drag_widget.hide()

    def mouseMoveEvent(self, event) -> None:
        self.drag_widget.move(event.pos())
        self.drag_widget.show()
        row, col = self.indexAt(event.pos()).row(), self.indexAt(event.pos()).column()
        # 设置当前行的背景色
        self.set_row_bg_color(row, QColor(254, 163, 86))
        # 还原上下行的背景色
        self.set_row_bg_color(row + 1, QColor(255,255,255))
        self.set_row_bg_color(row - 1, QColor(255,255,255))

    def set_row_bg_color(self, row, color):
        if row < 0:
            return
        for col in range(self.columnCount()):
            item = self.item(row, col)
            if item:
                item.setBackground(QBrush(color))

    def mousePressEvent(self, event) -> None:
        row, col = self.indexAt(event.pos()).row(), self.indexAt(event.pos()).column()
        cur_item = self.item(row, col)
        if col == 0 and cur_item:
            drag_row_data = cur_item.data(Qt.UserRole)  # 获取数据
            self.set_drag_data_on_widget(drag_row_data)
            # 记录老数据行号
            self.drag_row = row

        super(DraggableTableWidget, self).mousePressEvent(event)

    def mouseReleaseEvent(self, event) -> None:
        row, col = self.indexAt(event.pos()).row(), self.indexAt(event.pos()).column()

        # 还原当前行背景色
        self.set_row_bg_color(row, QColor(255, 255, 255))
        # 删除原行
        self.removeRow(self.drag_row)
        # 插入目标位置
        row_data = getattr(self.drag_widget, 'row_data', None)
        print(row_data)
        if row_data:
            self.insert_row_data(row, row_data)
            self.selectRow(row)
        self.init_drag_widget()
        super(DraggableTableWidget, self).mouseReleaseEvent(event)

    def get_table_data(self):
        data = []
        s = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
        for i in range(2101, 2120):
            data.append({'id': i, 'name': s[i - 2100], 'value': random.randint(1, 200)})
        return data

    def set_data(self):
        self.setColumnCount(4)
        self.horizontalHeader().setSectionResizeMode(3, QHeaderView.ResizeToContents)
        for row, row_item in enumerate(self.get_table_data()):
            self.insert_row_data(row, row_item)

    def insert_row_data(self, row, row_item):
        self.insertRow(row)
        for col, key in enumerate(self.data_keys + ['']):
            if col == 3:
                item = QTableWidgetItem()
                item.setIcon(QIcon('media/icons/delete.png'))
            else:
                item = QTableWidgetItem(str(row_item[key]))

            if col == 0:
                item.setData(Qt.UserRole, row_item)
            self.setItem(row, col, item)



# QCoreApplication.setAttribute(Qt.AA_EnableHighDpiScaling)  # 高分辨率DPI屏幕自动缩放

app = QApplication([])
t = DraggableTableWidget()
t.get_table_data()

t.set_data()
t.show()
sys.exit(app.exec_())

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值