PyQt5 QTableWidget总结

一、学习笔记

(一)、导入模板及设置界面

import sys

from PyQt5.QtWidgets import QApplication,QWidget,QTableWidget,QMainWindow,QTableWidgetItem,QAbstractItemView,QComboBox
from PyQt5.QtGui import QFont,QColor
from PyQt5.QtCore import Qt
class MainWindow(QMainWindow):
    def __init__(self, parent=None):
        super(MainWindow, self).__init__(parent)
        self.setWindowTitle("QTabWidget")
        self.resize(800,600)
        self.setUI()
    def setUI(self):
        tab=QTableWidget(self)
        tab.resize(self.size().width(),self.size().height())

(二)、属性和方法

 1.设置行数和列数

        # 1.设置行数和列数
        tab.setRowCount(6)
        tab.setColumnCount(5)

2.设置列标题和行标题

        # 2.设置列标题和行标题
        tab.setHorizontalHeaderLabels(['姓名','语文','数学','英语','地理','历史'])
        tab.setVerticalHeaderLabels(['A','B','C','D','E'])

3.添加单元格数据__文本

        # 3.添加单元格数据__文本
        item1=QTableWidgetItem('张冬冬')
        tab.setItem(0,0,item1)

4.设置单元格字体和颜色

        # 4.设置单元格字体和颜色 P69
        item2=QTableWidgetItem('良好')
        item2.setFont(QFont('黑体', 26))
        item2.setForeground(QColor(Qt.blue))
        tab.setItem(0,2,item2)

5.设置单元格对齐方式

        # 5.设置单元格对齐方式 P71
        item3=QTableWidgetItem('不及格')
        item3.setTextAlignment(Qt.AlignRight)
        tab.setItem(0,3,item3)

6.禁止编辑

        # 6.禁止编辑
        tab.setEditTriggers(QAbstractItemView.NoEditTriggers)

7.整行选择、整列选择

        # 7.整行选择、整列选择
        tab.setSelectionBehavior(QAbstractItemView.SelectRows)
        tab.setSelectionBehavior(QAbstractItemView.SelectColumns)

8.根据内容自动行高和列宽

        # 8.根据内容自动行高和列宽
        tab.resizeRowsToContents()
        tab.resizeColumnsToContents()

9.隐藏行头和列头

        # 9.隐藏行头和列头
        tab.horizontalHeader().setVisible(False)
        tab.verticalHeader().setVisible(False)

10.隐藏表格线

        # 10.隐藏表格线
        tab.setShowGrid(True)

11.向单元格中添加控件

        # 11.向单元格中添加控件
        combox=QComboBox()
        combox.addItems(['男','女'])
        tab.setCellWidget(0,1,combox)

12.合并单元格

        # 12.合并单元格
        tab.setSpan(0,0,3,1)
        tab.setSpan(2,2,4,3)
        '''        
        tab.setSpan(row,column,rowSpan,columnSpan)
        参数
            row:单元格所在的行号
            column:单元格所在的列号
            rowSpan:向下要合并的行数
            columnSpan:向右要合并的列数            
        '''

13.设置行高和列宽

        # 13.设置行高和列宽
        tab.setRowHeight(1,60)
        tab.setColumnWidth(2,100)
        '''
        tab.setRowHeight(row,height)
        参数:
            row:要设置的行号
            height:要设置的行高
        tab.setColumnWidth() 参数用法同设置行高
        '''

14获取行高和列宽

        # 14获取行高和列宽
        height=tab.rowHeight(1)
        width=tab.columnWidth(2)
        '''
        tab.rowHeight(1)
            参数row表示所在的行号,获取列宽的参数设置不获取行高的参数设置
        '''
        print(height,width)

15设置边框颜色

        # 15设置边框颜色
        tab.setStyleSheet('QTableWidget{boder:2px splid black;}')

16.插入行

        # 16.插入行
        tab.insertRow(1)

17.获取单元格数据

        # 17.获取单元格数据
        get_item1=tab.item(0,0)
        print(get_item1.text())

18.获取单元格中控件的属性

        get_cellWidget=tab.cellWidget(0,1)
        print(get_cellWidget.size())

19.删除行删除列

        # 19.删除行删除列
        tab.removeRow(6)
        tab.removeColumn(4)

(三)、运行App

if __name__ == "__main__":
    app = QApplication(sys.argv)
    window = MainWindow()
    window.show()
    app.exec_()

二、解决实际问题的代码

(一)绘制QTableWidget单元格边框线

from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from PyQt5.QtGui import *

class LineDrawingDelegate(QStyledItemDelegate):
    def paint(self, painter, option, index):
        super().paint(painter, option, index)

        painter.setPen(QPen(Qt.blue, 1, Qt.SolidLine))

        if index.row()==table_widget.rowCount()-1 and index.column()==table_widget.columnCount()-1:
            pass
            painter.drawLine(option.rect.left(), option.rect.top(), option.rect.right(), option.rect.top()) # 上边线
            painter.drawLine(option.rect.left(), option.rect.bottom(), option.rect.right(), option.rect.bottom()) # 下边线
            painter.drawLine(option.rect.left(), option.rect.top(), option.rect.left(), option.rect.bottom()) # 左边线
            painter.drawLine(option.rect.right(), option.rect.top(), option.rect.right(), option.rect.bottom()) # 右边线
        elif index.row() != table_widget.rowCount() - 1 and index.column() != table_widget.columnCount() - 1:
            pass
            painter.drawLine(option.rect.left(), option.rect.top(), option.rect.right(), option.rect.top())  # 上边线
            # painter.drawLine(option.rect.left(), option.rect.bottom(), option.rect.right(), option.rect.bottom())  # 下边线
            painter.drawLine(option.rect.left(), option.rect.top(), option.rect.left(), option.rect.bottom())  # 左边线
            # painter.drawLine(option.rect.right(), option.rect.top(), option.rect.right(), option.rect.bottom())  # 右边线

        elif index.row() == table_widget.rowCount() - 1 and index.column() != table_widget.columnCount() - 1:
            pass
            painter.drawLine(option.rect.left(), option.rect.top(), option.rect.right(), option.rect.top()) # 上边线
            painter.drawLine(option.rect.left(), option.rect.bottom(), option.rect.right(), option.rect.bottom()) # 下边线
            painter.drawLine(option.rect.left(), option.rect.top(), option.rect.left(), option.rect.bottom()) # 左边线
            # painter.drawLine(option.rect.right(), option.rect.top(), option.rect.right(), option.rect.bottom()) # 右边线

        elif index.row() != table_widget.rowCount() - 1 and index.column() == table_widget.columnCount() - 1:
            pass
            painter.drawLine(option.rect.left(), option.rect.top(), option.rect.right(), option.rect.top()) # 上边线
            # painter.drawLine(option.rect.left(), option.rect.bottom(), option.rect.right(), option.rect.bottom()) # 下边线
            painter.drawLine(option.rect.left(), option.rect.top(), option.rect.left(), option.rect.bottom()) # 左边线
            painter.drawLine(option.rect.right(), option.rect.top(), option.rect.right(), option.rect.bottom()) # 右边线

app = QApplication([])

table_widget = QTableWidget()

# 设置行数和列数
table_widget.setRowCount(5)
table_widget.setColumnCount(5)

# 设置委托
table_widget.setItemDelegate(LineDrawingDelegate())
table_widget.show()
app.exec_()

(二)设置QTableWidget单元格填充柄

import sys

from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from PyQt5 import QtCore
import logging


class MyTableWidget(QTableWidget):
    dragbottom = pyqtSignal(object)

    def __init__(self, parent):
        super().__init__(parent)
        self.cellBottomRight = False
        self.leftbuttonDown = False

    def paintEvent(self, e):
        super().paintEvent(e)
        painter = QPainter(self.viewport())
        # painter.save();
        pen = painter.pen();
        pen.setWidth(1);
        selections = self.selectionModel()
        if selections != None:
            # logging.info("not None")
            list1 = selections.selectedIndexes()
            for i in range(len(list1)):
                modelIndex = list1[i]
                # QRect类提供各种矩形坐标,绘制线跟点的时候需要用到坐标
                rect = self.visualRect(modelIndex);
                tmpRect = QtCore.QRect(QtCore.QPoint(rect.x() + 1, rect.y() + 1),
                                       QtCore.QSize(rect.width() - 2, rect.height() - 2))

                # 如果是选中状态 并且在选择公式状态
                # if (self.item(i,j).isSelected()):
                # 给选中单元格进行画线画点
                dashes = []
                penXu = painter.pen();
                # 设置画笔宽度
                penXu.setWidth(2);
                color = QColor();
                # 设置画笔颜色
                color.setRgb(31, 187, 125);
                penXu.setColor(color);
                painter.setPen(penXu);
                # 绘制单元格四周的线
                painter.drawRect(tmpRect);
                # 绘制单元格右下角点
                penXu.setWidth(6);
                painter.setPen(penXu);
                painter.drawPoint(tmpRect.x() + tmpRect.width() - 3, tmpRect.y() + tmpRect.height() - 3);
        # 恢复QPainter对象
        # painter.restore();
        # self.viewport().update();

    # 鼠标移动事件
    def mouseMoveEvent(self, event):
        # 获取鼠标位置信息
        if not self.cellBottomRight:
            mousePos = event.pos();
            # 获取所有选中单元格
            # logging.info(self.cellBottomRight)
            itemList = self.selectedItems();
            # 没有选中单元格 就退出
            if len(itemList) <= 0:
                return;
            modelIndex = self.indexFromItem(itemList[len(itemList) - 1]);
            # 获取最后一个选中的单元格的QRect,用来判断是否鼠标位置是否在右下角区域
            rect = self.visualRect(modelIndex);
            # logging.info([mousePos,rect])
            # 判断是否在我们规定的区域,或者是按下模式,isClick是按下模式标志
            # print(dir(mousePos))
            if ((mousePos.x() >= (rect.x() + rect.width() - 7) and mousePos.x() <= (rect.x() + rect.width())
                 and mousePos.y() >= (rect.y() + rect.height() - 7) and mousePos.y() <= (rect.y() + rect.height()))):
                # logging.info("right bottom")
                # 设置鼠标在右下角区域样式
                self.setCursor(Qt.CrossCursor);
                # 在右下角区域
                self.cellBottomRight = True;
        super().mouseMoveEvent(event);
        # 鼠标点击事件

    def mousePressEvent(self, event):
        if (event.button() == Qt.LeftButton):
            self.leftbuttonDown = True;
            self.setCursor(Qt.SizeAllCursor);
        else:
            self.leftbuttonDown = False;
            self.setCursor(Qt.ArrowCursor);
        super().mousePressEvent(event);  # 提交鼠标给控件

    def mouseReleaseEvent(self, ev):
        if self.leftbuttonDown and self.cellBottomRight:
            itemList = self.selectedItems();
            if len(itemList) > 0:
                logging.info("dragbottom")
                self.dragbottom.emit(itemList)
        self.leftbuttonDown = False;
        self.cellBottomRight = False;
        self.setCursor(Qt.ArrowCursor);
        super().mouseReleaseEvent(ev)

class Table(QWidget):
    def __init__(self):
        super().__init__()
        self.setGeometry(300, 300, 500, 400)
        self.setWindowTitle('QTableWidget的基本用法')

        layout = QHBoxLayout()
        self.table = MyTableWidget(None)
        self.table.dragbottom.connect(self.dragbottom_trig)
        m = 4
        n = 3
        self.table.setRowCount(4)
        self.table.setColumnCount(3)
        layout.addWidget(self.table)
        self.table.setHorizontalHeaderLabels(['姓名', '性别', '体重(kg)'])
        for i in range(m):
            for j in range(n):
                item1 = QTableWidgetItem('')
                self.table.setItem(i, j, item1)
        item1 = QTableWidgetItem('张三')
        self.table.setItem(0, 0, item1)
        item2 = QTableWidgetItem('男')
        self.table.setItem(0, 1, item2)
        item3 = QTableWidgetItem('50')
        self.table.setItem(0, 2, item3)
        self.setLayout(layout)

    def dragbottom_trig(self, items):
        print(items) # 此代码可以不需要
        first = items[0]
        col = first.column()
        row = first.row()
        print(dir(first)) # 此代码可以不需要
        v = first.text()
        for one in items[1:]:
            self.table.setItem(one.row(), one.column(), QTableWidgetItem(v))

if __name__ == "__main__":
    app = QApplication(sys.argv)
    form = Table()
    form.show()
    sys.exit(app.exec_())

(三)QTableWidget右键复制粘贴行

from PyQt5.QtWidgets import QApplication, QMenu
from PyQt5.QtCore import Qt
from PyQt5.QtGui import QCursor
from PyQt5 import QtWidgets
from PyQt5.Qt import QTableWidgetItem

class MyTableWidget(QtWidgets.QTableWidget):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.setContextMenuPolicy(Qt.CustomContextMenu)  # 创建右键菜单信号事件
        self.customContextMenuRequested.connect(self.show_menu)

        self.context_menu = QMenu(self)
        self.cp = self.context_menu.addAction('复制')
        self.jq = self.context_menu.addAction('剪切')
        self.nt = self.context_menu.addAction('粘贴')

        self.cp.triggered.connect(self.copy)
        self.jq.triggered.connect(self.cut)
        self.nt.triggered.connect(self.paste)

    def del_tb_text(self):
        try:
            indexes = self.selectedIndexes()
            for index in indexes:
                row, column = index.row(), index.column()
                item = QTableWidgetItem()
                self.setItem(row, column, item)
        except BaseException as e:
            print(e)
            return

    def paste_tb_text(self):
        try:
            indexes = self.selectedIndexes()
            for index in indexes:
                index = index
                break
            r, c = index.row(), index.column()

            text = QApplication.clipboard().text()
            ls = text.split('\n')
            ls1 = []
            for row in ls:
                ls1.append(row.split('\t'))

            rows = len(ls)
            columns = len(ls1[0])

            for row in range(rows):
                for column in range(columns):
                    item = QTableWidgetItem()
                    item.setText((str(ls1[row][column])))
                    self.setItem(row + r, column + c, item)
        except Exception as e:
            print(e)
            return

    def selected_tb_text(self):
        try:
            indexes = self.selectedIndexes()  # 获取表格对象中被选中的数据索引列表
            indexes_dict = {}
            for index in indexes:  # 遍历每个单元格
                row, column = index.row(), index.column()  # 获取单元格的行号,列号
                if row in indexes_dict.keys():
                    indexes_dict[row].append(column)
                else:
                    indexes_dict[row] = [column]

            # 将数据表数据用制表符(\t)和换行符(\n)连接,使其可以复制到 Excel 文件中
            text = ''
            for row, columns in indexes_dict.items():
                row_data = ''
                for column in columns:
                    data = self.item(row, column).text()
                    if row_data:
                        row_data = row_data + '\t' + data
                    else:
                        row_data = data
                if text:
                    text = text + '\n' + row_data
                else:
                    text = row_data
            return text
        except BaseException as e:
            print(e)
            return ''

    def copy(self):
        text = self.selected_tb_text()  # 获取当前表格选中的数据
        if text:
            clipboard = QApplication.clipboard()
            clipboard.setText(text)

    def cut(self):
        self.copy()
        self.del_tb_text()

    def paste(self):
        self.paste_tb_text()

    def show_menu(self, pos):
        # pos 鼠标位置
        # 菜单显示前,将它移动到鼠标点击的位置
        self.context_menu.exec_(QCursor.pos())  # 在鼠标位置显示

    def keyPressEvent(self, event):  # 重写键盘监听事件
        # 监听 Ctrl+C 组合键,实现复制数据到粘贴板
        if (event.key() == Qt.Key_C) and QApplication.keyboardModifiers() == Qt.ControlModifier:
            self.copy()  # 获取当前表格选中的数据
        elif (event.key() == Qt.Key_X) and QApplication.keyboardModifiers() == Qt.ControlModifier:
            self.cut()
        elif (event.key() == Qt.Key_V) and QApplication.keyboardModifiers() == Qt.ControlModifier:
            self.paste()
        elif (event.key() == Qt.Key_Z) and QApplication.keyboardModifiers() == Qt.ControlModifier:
            self.paste()
        else:
            super().keyPressEvent(event)
app = QtWidgets.QApplication([])
table_widget = MyTableWidget(10,6)
# table_widget
# 设置表格的行数和列数等其他操作...
table_widget.show()
app.exec_()

(四)读取广联达工程量清单,换行显示项目特征


import openpyxl
from PyQt5.QtWidgets import *

import sys

import pandas as pd
from PyQt5.QtWidgets import QApplication, QWidget, QMainWindow, QVBoxLayout, QLabel, QLineEdit, QFormLayout, \
    QTableWidget, QDesktopWidget, QHBoxLayout, QPushButton, QFileDialog, QTextEdit, QMenu, QComboBox, QMessageBox
from PyQt5.QtGui import QFont, QIcon

class myWin(QMainWindow):
    def __init__(self):
        super(myWin, self).__init__()
        self.setWindowTitle('易造价')
        # 设置窗口尺寸为电脑屏幕尺寸
        desktop = QDesktopWidget()
        screen_width = desktop.screenGeometry().width()
        screen_height = desktop.screenGeometry().height()

        self.resize(int(screen_width / 2), int(screen_height / 2))
        self.setMenu()

    def setMenu(self):
        self.bar = self.menuBar()
        self.bar.setFont(QFont('楷书', 14))
        # 文件菜单
        file = self.bar.addMenu('文件')
        file.setFont(QFont('楷书', 12))

        f2=file.addAction('测试QTableWidget是否自动换行')
        f2.triggered.connect(self.TestTableWidgetWrap)

    def TestTableWidgetWrap(self): # 读取广联达工程量清单的程序,功能:1.可以换行显示项目特征。2.根据行数自动调整行高
        print('测试换行')
        self.setOption_QWidget=QWidget()

        self.setOption_QWidget.setWindowTitle('测试QTableWidget单元格识别换行文本')
        self.setOption_QWidget.resize(800, 600)
        setOption_mainLay=QVBoxLayout()
        self.setOption_QWidget.setLayout(setOption_mainLay)

        self.table1=QTableWidget()
        setOption_mainLay.addWidget(self.table1)
        self.table1.resize(self.setOption_QWidget.width(),self.setOption_QWidget.height())

        path1, _ = QFileDialog.getOpenFileName(self, '打开文件', 'H:/Copy', 'Excel文件(*.xlsx)')
        # print()
        # print(path1)
        self.xlsxBook = openpyxl.load_workbook(path1)
        self.xlsxSheet1 = self.xlsxBook.active
        # print(self.xlsxBook.sheetnames)
        self.row_num = self.xlsxSheet1.max_row
        df_list=[]

        list1=['C10','C15','C20','C25','C30','C35','C40','C45','C50','C55','C60','C65','C70','C75','C80']
        for i in range(self.row_num):
            cell_A = self.xlsxSheet1.cell(row=i + 1, column=1)
            cell_B = self.xlsxSheet1.cell(row=i + 1, column=2)
            cell_C = self.xlsxSheet1.cell(row=i + 1, column=3)
            cell_D = self.xlsxSheet1.cell(row=i + 1, column=4)
            cell_E = self.xlsxSheet1.cell(row=i + 1, column=5)
            cell_F = self.xlsxSheet1.cell(row=i + 1, column=6)
            cell_G = self.xlsxSheet1.cell(row=i + 1, column=7)
            if cell_B.value is None:
                pass
            else:
                if len(cell_B.value)==12:
                    dict1 = {'编码': cell_B.value,'项目名称':cell_C.value,'项目特征':cell_D.value, '工程量': cell_G.value, '单位': cell_F.value}
                    df_list.append(dict1)

        df=pd.DataFrame(df_list)
        # print(df)
        self.table1.setRowCount(len(df))
        self.table1.setColumnCount(5)
        for i in range(len(df)):
            for j in range(5):
                item1 = QTableWidgetItem(df.iat[i, j])
                self.table1.setItem(i,j,item1)
        header_list=['编码','项目名称','项目特征','工程量','单位']
        self.table1.setHorizontalHeaderLabels(header_list)
        for i in range(self.table1.rowCount()):
            self.table1.setColumnWidth(0, 100)
            self.table1.setColumnWidth(1, 100)
            self.table1.setColumnWidth(2, 150)
            self.table1.setColumnWidth(3, 100)
            self.table1.setColumnWidth(4, 50)
        self.table1.resizeRowsToContents()
        for i in range(self.table1.rowCount()):
            height=self.table1.rowHeight(i)
            # print(height)
            self.table1.setRowHeight(i,height+15)
        self.setOption_QWidget.show()


if __name__ == '__main__':
    app = QApplication(sys.argv)
    w = myWin()
    w.show()
    app.exec()
'''
PyQt5开发三维建模项目的思路畅想:
    1.使用QPainter类,加上QWidget坐标系统功能,完全或以绘制图元的分布图,标注哪个图元的审核误差值是多少。
    2.使用该类加上鼠标事件,完全可以绘制平面图,实现构件计算工程量。
    3.若可以读取CAD文件中的内容,则可以实现导入CAD图计算工程量的功能。
    4.绘制梁只不过需要定义梁构件的类即可,这样就可以一键画梁了,绘制其他图元也是图样道理。
    5.勾选图层功能,使用复选框架,显示/优选显示某些线条而已。
    6.PyQt完全具备实现展示图形的三维效果功能。参考代码:https://www.jb51.net/python/315146ubj.htm
'''

(五)QTableWidget设置单元格中控件居中的办法


from PyQt5.QtGui import *
import sys
from PyQt5.QtWidgets import QApplication, QWidget, QMainWindow,QTableWidget, QDesktopWidget, QHBoxLayout,QCheckBox
from PyQt5.QtCore import Qt

class myWin(QMainWindow):
    def __init__(self):
        super(myWin, self).__init__()
        self.setWindowTitle('易造价')
        # 设置窗口尺寸为电脑屏幕尺寸
        desktop = QDesktopWidget()
        screen_width = desktop.screenGeometry().width()
        screen_height = desktop.screenGeometry().height()

        self.resize(int(screen_width / 2), int(screen_height / 2))
        table=QTableWidget(self)
        table.resize(int(self.width()),int(self.height()))
        table.setRowCount(8)
        table.setColumnCount(6)
        w=QWidget()
        l=QHBoxLayout()
        w.setLayout(l)
        c=QCheckBox()
        # c.addItems(['男','女'])
        l.addWidget(c)
        l.setAlignment(Qt.AlignHCenter)
        table.setCellWidget(0,0,w)
        table.resizeRowsToContents()
        cw=table.cellWidget(0,0)
        cwl=cw.layout()
        print()

if __name__ == '__main__':
    app = QApplication(sys.argv)
    w = myWin()
    w.show()
    app.exec()

(六)QTableWidget填充柄,可以自动填充数字二/(二)内容代码升级

import sys

from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from PyQt5 import QtCore
def JudgeStrDataType(str1):
    '''
    大致判断字符串中包含的数据类型,为以后的程序开发中判断字符中包含的数据类型,提供思路参考
    :param str1: 参数为一个字符串类型
    :return: 返回值是一个包含三个元素的元组
            第一个元素值的含义
                1   不带正号的正整数类型
                2   不包含数字的字符串
                3   字符串中同时存在数字和字符串
                4   空字符串
                5   其他情况,一般不会出现该值
            第二个元素值的含义
                如果是数字,则表示最右侧一个数字在字符串中的切片位置;
                如果是None,则表示该字符串中无数字
            第三个元素值的含
                如果是数字,则表示该数字是字符串中从右侧数第一个数字,该返回值的数据类型是int
                如果是None,则表示该字符串中无数字
    '''
    jugde_list=[]
    for i in str1:
        try:
            if type(eval(i))==int:
                jugde_list.append(True)
        except:
            jugde_list.append(False)
    jugde_set=set(jugde_list)

    if len(jugde_set)==1 and True in jugde_set:
        # print('整数类型')
        return (1,len(str1)-1,eval(str1[-1]))
    elif len(jugde_set)==1 and False in jugde_set:
        # print('不包含数字的字符串')
        return (2,None,None)
    elif True in jugde_list and False in jugde_list:
        global x
        for pos in range(len(str1)):

            try:
                if type(eval(str1[-pos-1]))==int:
                    x=len(str1)-pos-1
                    break
            except:
                pass
        # print('字符数字同在')
        return (3,x,eval(str1[x]))
    elif str1=='':
        # print('None,None')
        return (4,None,None)
    else:
        # print('其他')
        return (5,None,None)

class BorderedItemDelegate(QStyledItemDelegate):
    def paint(self, painter, option, index):
        # 设置绘制选项
        option.displayAlignment = Qt.AlignCenter
        # 绘制背景
        super(BorderedItemDelegate, self).paint(painter, option, index)

        # 绘制边框
        rect = option.rect
        border_color = Qt.blue
        border_width = 1
        painter.save()
        painter.setPen(QPen(border_color, border_width))
        # painter.drawRect(rect.adjusted(0, 0, 0, 0))  # 减去1像素来避免在边缘绘制时被裁剪

        if index.row() == 0 and index.column() == 0:
            pass
            painter.drawLine(option.rect.left(), option.rect.top(), option.rect.right(), option.rect.top())  # 上边线
            painter.drawLine(option.rect.left(), option.rect.bottom(), option.rect.right(), option.rect.bottom())  # 下边线
            painter.drawLine(option.rect.left(), option.rect.top(), option.rect.left(), option.rect.bottom())  # 左边线
            painter.drawLine(option.rect.right(), option.rect.top(), option.rect.right(), option.rect.bottom())  # 右边线
        elif index.row() != 0 and index.column() == 0:
            pass
            # painter.drawLine(option.rect.left(), option.rect.top(), option.rect.right(), option.rect.top())  # 上边线
            painter.drawLine(option.rect.left(), option.rect.bottom(), option.rect.right(), option.rect.bottom())  # 下边线
            painter.drawLine(option.rect.left(), option.rect.top(), option.rect.left(), option.rect.bottom())  # 左边线
            painter.drawLine(option.rect.right(), option.rect.top(), option.rect.right(), option.rect.bottom())  # 右边线
        elif index.row() == 0 and index.column() != 0:
            painter.drawLine(option.rect.left(), option.rect.top(), option.rect.right(), option.rect.top())  # 上边线
            painter.drawLine(option.rect.left(), option.rect.bottom(), option.rect.right(), option.rect.bottom())  # 下边线
            # painter.drawLine(option.rect.left(), option.rect.top(), option.rect.left(), option.rect.bottom())  # 左边线
            painter.drawLine(option.rect.right(), option.rect.top(), option.rect.right(), option.rect.bottom())  # 右边线
        elif index.row() != 0 and index.column() != 0:
            # painter.drawLine(option.rect.left(), option.rect.top(), option.rect.right(), option.rect.top())  # 上边线
            painter.drawLine(option.rect.left(), option.rect.bottom(), option.rect.right(), option.rect.bottom())  # 下边线
            # painter.drawLine(option.rect.left(), option.rect.top(), option.rect.left(), option.rect.bottom())  # 左边线
            painter.drawLine(option.rect.right(), option.rect.top(), option.rect.right(), option.rect.bottom())  # 右边线

class MyTableWidget(QTableWidget):
    dragbottom = pyqtSignal(object)

    def __init__(self):
        super().__init__()
        self.cellRightBottom = False # 定义一个变量,判断光标指针是否在单元格右下角,此变量名称原来是self.cellBottomRight。
        self.leftbuttonDown = False # 定义一个变量,判断鼠标左键是否按下。
        self.setContextMenuPolicy(Qt.CustomContextMenu)
        self.setItemDelegate(BorderedItemDelegate())

        # self.setItemDelegate()
        self.customContextMenuRequested.connect(self.generate_menu)
    def generate_menu(self, pos):
        self.menu = QMenu()
        delete_action = self.menu.addAction("删除选中行")
        delete_action.triggered.connect(self.RightMenuDeleteRow)
        add_action = self.menu.addAction("末尾添加行")
        add_action.triggered.connect(self.RightMenuAddRow)

        top_insert_action=self.menu.addAction('上部添加行')
        top_insert_action.triggered.connect(self.RightMenuTopInsertRow)
        bottom_insert_action=self.menu.addAction('下部添加行')
        bottom_insert_action.triggered.connect(self.RightMenuBottomInsertRow)
        copy_action=self.menu.addAction('复制行')
        copy_action.triggered.connect(self.copyRow)
        paste_action=self.menu.addAction('粘贴行')
        paste_action.triggered.connect(self.pasteRow)

        self.menu.exec_(self.mapToGlobal(pos))

    def RightMenuDeleteRow(self):
        current_row = self.currentRow()
        if current_row >= 0:
            self.removeRow(current_row)
    def RightMenuAddRow(self):
        rowNum=self.rowCount()
        addNum=rowNum+1
        self.setRowCount(addNum)
    def RightMenuTopInsertRow(self):
        self.insertRow(self.currentRow())
    def RightMenuBottomInsertRow(self,pos):
        self.insertRow(self.currentRow()+1)
    def copyRow(self):
        # 获取当前选中的行
        currentRow = self.currentRow()
        if currentRow < 0:
            return

        # 获取剪贴板数据
        clipboard = QApplication.clipboard()
        clipboardData = ""
        for col in range(self.columnCount()):
            item = self.item(currentRow, col)
            if item is not None:
                clipboardData += str(item.text())
                if col < self.columnCount() - 1:
                    clipboardData += "\t"
        clipboard.setText(clipboardData)

    def pasteRow(self):
        # 获取当前选中的行
        currentRow = self.currentRow()
        if currentRow < 0:
            return

        # 获取剪贴板数据
        clipboard = QApplication.clipboard()
        clipboardData = clipboard.text()

        # 分割剪贴板数据并设置到表格中
        rows = clipboardData.split("\n")
        for row in rows:
            columns = row.split("\t")
            for col in range(len(columns)):
                item = QTableWidgetItem(columns[col])
                self.setItem(currentRow, col, item)
            currentRow += 1

    def mousePressEvent(self, event): # 判断鼠标左键是否按下,并设置光标形状
        if (event.button() == Qt.LeftButton):
            self.leftbuttonDown = True;
            self.setCursor(Qt.SizeAllCursor);
        else:
            self.leftbuttonDown = False;
            self.setCursor(Qt.ArrowCursor);
        super().mousePressEvent(event);  # 提交鼠标给控件

    def paintEvent(self, e): # 绘制选中单元格边框及填充柄
        super().paintEvent(e)
        painter = QPainter(self.viewport())
        pen = painter.pen();
        pen.setWidth(1);
        selections = self.selectionModel()
        if selections != None:
            # logging.info("not None")
            list1 = selections.selectedIndexes()
            for i in range(len(list1)):
                modelIndex = list1[i]
                # QRect类提供各种矩形坐标,绘制线跟点的时候需要用到坐标
                rect = self.visualRect(modelIndex);
                tmpRect = QtCore.QRect(QtCore.QPoint(rect.x() + 1, rect.y() + 1),
                                       QtCore.QSize(rect.width() - 2, rect.height() - 2))

                penXu = painter.pen();
                # 设置画笔宽度
                penXu.setWidth(2);
                color = QColor();
                # 设置画笔颜色
                color.setRgb(31, 187, 125);
                penXu.setColor(color);
                painter.setPen(penXu);
                # 绘制单元格四周的线
                painter.drawRect(tmpRect);
                # 绘制单元格右下角点
                penXu.setWidth(6);
                painter.setPen(penXu);
                painter.drawPoint(tmpRect.x() + tmpRect.width() - 3, tmpRect.y() + tmpRect.height() - 3);
    # 鼠标移动事件
    def mouseMoveEvent(self, event): # 判断鼠标指标是否位于单元格填充柄位置
        # 获取鼠标位置信息
        if not self.cellRightBottom:
            mousePos = event.pos();
            # 获取所有选中单元格
            itemList = self.selectedItems();
            # 没有选中单元格 就退出
            if len(itemList) <= 0:
                return;
            modelIndex = self.indexFromItem(itemList[len(itemList) - 1]);
            # 获取最后一个选中的单元格的QRect,用来判断是否鼠标位置是否在右下角区域
            rect = self.visualRect(modelIndex);
            # 判断是否在我们规定的区域,或者是按下模式,isClick是按下模式标志
            if ((mousePos.x() >= (rect.x() + rect.width() - 7) and mousePos.x() <= (rect.x() + rect.width())
                 and mousePos.y() >= (rect.y() + rect.height() - 7) and mousePos.y() <= (rect.y() + rect.height()))):
                # 设置鼠标在右下角区域样式
                self.setCursor(Qt.CrossCursor);
                # 在右下角区域
                self.cellRightBottom = True;
        super().mouseMoveEvent(event);
        # 鼠标点击事件

    def mouseReleaseEvent(self, ev): # 自定义信息的发射条件
        if self.leftbuttonDown and self.cellRightBottom:
            itemList = self.selectedItems();
            if len(itemList) > 0:
                self.dragbottom.emit(itemList)
        self.leftbuttonDown = False;
        self.cellRightBottom = False;
        self.setCursor(Qt.ArrowCursor);
        super().mouseReleaseEvent(ev)

class Table(QWidget):

    def __init__(self):
        super().__init__()
        self.setGeometry(300, 300, 500, 400)
        self.setWindowTitle('QTableWidget的基本用法')

        layout = QHBoxLayout()
        self.table = MyTableWidget()
        self.table.dragbottom.connect(self.dragbottom_trig)
        self.table.setRowCount(8)
        self.table.setColumnCount(6)
        layout.addWidget(self.table)
        for i in range(self.table.rowCount()):
            for j in range(self.table.columnCount()):
                item1 = QTableWidgetItem('')
                self.table.setItem(i, j, item1)
        self.setLayout(layout)


    def dragbottom_trig(self, items):
        first = items[0]
        v = first.text()
        item_list=items[1:]
        item_num=len(item_list)
        list1 = []
        for i in range(item_num):
            a, b, c = JudgeStrDataType(v)
            if a == 1:
                new_str1 = str(eval(v) + i+1)
                list1.append(new_str1)
            elif a == 2 or a == 4 or a == 5:
                list1.append(v)
            elif a == 3:
                replace_str = c + i+1
                new_str2=''
                for k in range(len(v)):
                    if k==v.rfind(str(c)):
                        new_str2=new_str2+str(replace_str)
                    else:
                        new_str2=new_str2+v[k]
                list1.append(new_str2)
        print(list1)
        # 添加填充
        for one in item_list:
            pos_num=item_list.index(one)
            self.table.setItem(one.row(), one.column(), QTableWidgetItem(list1[pos_num]))



if __name__ == "__main__":
    app = QApplication(sys.argv)
    form = Table()
    form.show()
    sys.exit(app.exec_())

(七)重定QTableWidget类方法,制作单元格填充柄二.(二)、二.(六)小节的代码升级

注意事项:

设置单元格行数和列数时,必须使用如下方法:

        self.table = MyTableWidget()
        self.table.setRowCount(8)
        self.table.setColumnCount(6)

不允许使用如下设置行数和列数的方法,否则会报错:

self.table=MyTableWidget(self,6,8)

完整代码:

import sys

from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *

def JudgeStrDataType(str1):
    '''
    大致判断字符串中包含的数据类型,为以后的程序开发中判断字符中包含的数据类型,提供思路参考
    :param str1: 参数为一个字符串类型
    :return: 返回值是一个包含三个元素的元组
            第一个元素值的含义
                1   不带正号的正整数类型
                2   不包含数字的字符串
                3   字符串中同时存在数字和字符串
                4   空字符串
                5   其他情况,一般不会出现该值
            第二个元素值的含义
                如果是数字,则表示最右侧一个数字在字符串中的切片位置;
                如果是None,则表示该字符串中无数字
            第三个元素值的含
                如果是数字,则表示该数字是字符串中从右侧数第一个数字,该返回值的数据类型是int
                如果是None,则表示该字符串中无数字
    '''
    jugde_list=[]
    for i in str1:
        try:
            if type(eval(i))==int:
                jugde_list.append(True)
        except:
            jugde_list.append(False)
    jugde_set=set(jugde_list)

    if len(jugde_set)==1 and True in jugde_set:
        # print('整数类型')
        return (1,len(str1)-1,eval(str1[-1]))
    elif len(jugde_set)==1 and False in jugde_set:
        # print('不包含数字的字符串')
        return (2,None,None)
    elif True in jugde_list and False in jugde_list:
        global x
        for pos in range(len(str1)):

            try:
                if type(eval(str1[-pos-1]))==int:
                    x=len(str1)-pos-1
                    break
            except:
                pass
        # print('字符数字同在')
        return (3,x,eval(str1[x]))
    elif str1=='':
        # print('None,None')
        return (4,None,None)
    else:
        # print('其他')
        return (5,None,None)

class BorderedItemDelegate(QStyledItemDelegate):
    def paint(self, painter, option, index):
        # 设置绘制选项
        option.displayAlignment = Qt.AlignCenter
        # 绘制背景
        super(BorderedItemDelegate, self).paint(painter, option, index)

        # 绘制边框
        rect = option.rect
        border_color = Qt.blue
        border_width = 1
        painter.save()
        painter.setPen(QPen(border_color, border_width))
        # painter.drawRect(rect.adjusted(0, 0, 0, 0))  # 减去1像素来避免在边缘绘制时被裁剪

        if index.row() == 0 and index.column() == 0:
            pass
            painter.drawLine(option.rect.left(), option.rect.top(), option.rect.right(), option.rect.top())  # 上边线
            painter.drawLine(option.rect.left(), option.rect.bottom(), option.rect.right(), option.rect.bottom())  # 下边线
            painter.drawLine(option.rect.left(), option.rect.top(), option.rect.left(), option.rect.bottom())  # 左边线
            painter.drawLine(option.rect.right(), option.rect.top(), option.rect.right(), option.rect.bottom())  # 右边线
        elif index.row() != 0 and index.column() == 0:
            pass
            # painter.drawLine(option.rect.left(), option.rect.top(), option.rect.right(), option.rect.top())  # 上边线
            painter.drawLine(option.rect.left(), option.rect.bottom(), option.rect.right(), option.rect.bottom())  # 下边线
            painter.drawLine(option.rect.left(), option.rect.top(), option.rect.left(), option.rect.bottom())  # 左边线
            painter.drawLine(option.rect.right(), option.rect.top(), option.rect.right(), option.rect.bottom())  # 右边线
        elif index.row() == 0 and index.column() != 0:
            painter.drawLine(option.rect.left(), option.rect.top(), option.rect.right(), option.rect.top())  # 上边线
            painter.drawLine(option.rect.left(), option.rect.bottom(), option.rect.right(), option.rect.bottom())  # 下边线
            # painter.drawLine(option.rect.left(), option.rect.top(), option.rect.left(), option.rect.bottom())  # 左边线
            painter.drawLine(option.rect.right(), option.rect.top(), option.rect.right(), option.rect.bottom())  # 右边线
        elif index.row() != 0 and index.column() != 0:
            # painter.drawLine(option.rect.left(), option.rect.top(), option.rect.right(), option.rect.top())  # 上边线
            painter.drawLine(option.rect.left(), option.rect.bottom(), option.rect.right(), option.rect.bottom())  # 下边线
            # painter.drawLine(option.rect.left(), option.rect.top(), option.rect.left(), option.rect.bottom())  # 左边线
            painter.drawLine(option.rect.right(), option.rect.top(), option.rect.right(), option.rect.bottom())  # 右边线

class MyTableWidget(QTableWidget):
    dragbottom = pyqtSignal(object)
    rowCountChanged = pyqtSignal(int)  # 行数改变时的自定义信号
    columnCountChanged = pyqtSignal(int)  # 列数改变时的自定义信号

    def __init__(self):
        super().__init__()
        self.cellRightBottom = False # 定义一个变量,判断光标指针是否在单元格右下角,此变量名称原来是self.cellBottomRight。
        self.leftbuttonDown = False # 定义一个变量,判断鼠标左键是否按下。
        self.setContextMenuPolicy(Qt.CustomContextMenu)
        self.setItemDelegate(BorderedItemDelegate())
        self.dragbottom.connect(self.dragbottom_trig)
        self.rowCountChanged.connect(self.setItemValue)
        self.columnCountChanged.connect(self.setItemValue)
        # self.setItemDelegate()
        self.customContextMenuRequested.connect(self.generate_menu)
    def generate_menu(self, pos):
        self.menu = QMenu()
        delete_action = self.menu.addAction("删除选中行")
        delete_action.triggered.connect(self.RightMenuDeleteRow)
        add_action = self.menu.addAction("末尾添加行")
        add_action.triggered.connect(self.RightMenuAddRow)

        top_insert_action=self.menu.addAction('上部添加行')
        top_insert_action.triggered.connect(self.RightMenuTopInsertRow)
        bottom_insert_action=self.menu.addAction('下部添加行')
        bottom_insert_action.triggered.connect(self.RightMenuBottomInsertRow)
        copy_action=self.menu.addAction('复制行')
        copy_action.triggered.connect(self.copyRow)
        paste_action=self.menu.addAction('粘贴行')
        paste_action.triggered.connect(self.pasteRow)

        self.menu.exec_(self.mapToGlobal(pos))

    def RightMenuDeleteRow(self):
        current_row = self.currentRow()
        if current_row >= 0:
            self.removeRow(current_row)
    def RightMenuAddRow(self):
        rowNum=self.rowCount()
        addNum=rowNum+1
        self.setRowCount(addNum)
    def RightMenuTopInsertRow(self):
        self.insertRow(self.currentRow())
    def RightMenuBottomInsertRow(self,pos):
        self.insertRow(self.currentRow()+1)
    def copyRow(self):
        # 获取当前选中的行
        currentRow = self.currentRow()
        if currentRow < 0:
            return

        # 获取剪贴板数据
        clipboard = QApplication.clipboard()
        clipboardData = ""
        for col in range(self.columnCount()):
            item = self.item(currentRow, col)
            if item is not None:
                clipboardData += str(item.text())
                if col < self.columnCount() - 1:
                    clipboardData += "\t"
        clipboard.setText(clipboardData)

    def pasteRow(self):
        # 获取当前选中的行
        currentRow = self.currentRow()
        if currentRow < 0:
            return

        # 获取剪贴板数据
        clipboard = QApplication.clipboard()
        clipboardData = clipboard.text()

        # 分割剪贴板数据并设置到表格中
        rows = clipboardData.split("\n")
        for row in rows:
            columns = row.split("\t")
            for col in range(len(columns)):
                item = QTableWidgetItem(columns[col])
                self.setItem(currentRow, col, item)
            currentRow += 1

    def mousePressEvent(self, event): # 判断鼠标左键是否按下,并设置光标形状
        if (event.button() == Qt.LeftButton):
            self.leftbuttonDown = True;
            self.setCursor(Qt.SizeAllCursor);
        else:
            self.leftbuttonDown = False;
            self.setCursor(Qt.ArrowCursor);
        super().mousePressEvent(event);  # 提交鼠标给控件

    def paintEvent(self, e): # 绘制选中单元格边框及填充柄
        super().paintEvent(e)
        painter = QPainter(self.viewport())
        pen = painter.pen();
        pen.setWidth(1);
        selections = self.selectionModel()
        if selections != None:
            # logging.info("not None")
            list1 = selections.selectedIndexes()
            for i in range(len(list1)):
                modelIndex = list1[i]
                # QRect类提供各种矩形坐标,绘制线跟点的时候需要用到坐标
                rect = self.visualRect(modelIndex);
                tmpRect = QRect(QPoint(rect.x() + 1, rect.y() + 1),
                                       QSize(rect.width() - 2, rect.height() - 2))

                penXu = painter.pen();
                # 设置画笔宽度
                penXu.setWidth(2);
                color = QColor();
                # 设置画笔颜色
                color.setRgb(31, 187, 125);
                penXu.setColor(color);
                painter.setPen(penXu);
                # 绘制单元格四周的线
                painter.drawRect(tmpRect);
                # 绘制单元格右下角点
                penXu.setWidth(6);
                painter.setPen(penXu);
                painter.drawPoint(tmpRect.x() + tmpRect.width() - 3, tmpRect.y() + tmpRect.height() - 3);
    # 鼠标移动事件
    def mouseMoveEvent(self, event): # 判断鼠标指标是否位于单元格填充柄位置
        # 获取鼠标位置信息
        if not self.cellRightBottom:
            mousePos = event.pos();
            # 获取所有选中单元格
            itemList = self.selectedItems();
            # 没有选中单元格 就退出
            if len(itemList) <= 0:
                return;
            modelIndex = self.indexFromItem(itemList[len(itemList) - 1]);
            # 获取最后一个选中的单元格的QRect,用来判断是否鼠标位置是否在右下角区域
            rect = self.visualRect(modelIndex);
            # 判断是否在我们规定的区域,或者是按下模式,isClick是按下模式标志
            if ((mousePos.x() >= (rect.x() + rect.width() - 7) and mousePos.x() <= (rect.x() + rect.width())
                 and mousePos.y() >= (rect.y() + rect.height() - 7) and mousePos.y() <= (rect.y() + rect.height()))):
                # 设置鼠标在右下角区域样式
                self.setCursor(Qt.CrossCursor);
                # 在右下角区域
                self.cellRightBottom = True;
        super().mouseMoveEvent(event);
        # 鼠标点击事件

    def mouseReleaseEvent(self, ev): # 自定义信息的发射条件

        if self.leftbuttonDown and self.cellRightBottom:
            selectedIndexes = self.selectedIndexes()
            itemList=[self.item(index.row(),index.column()) for index in selectedIndexes]
            # itemList = self.selectedItems();
            if len(itemList) > 0:
                self.dragbottom.emit(itemList)
        self.leftbuttonDown = False;
        self.cellRightBottom = False;
        self.setCursor(Qt.ArrowCursor);
        super().mouseReleaseEvent(ev)
    def setRowCount(self, rowCount):
        super(MyTableWidget, self).setRowCount(rowCount)
        self.rowCountChanged.emit(rowCount)
    def setColumnCount(self, columnCount):
        super(MyTableWidget, self).setColumnCount(columnCount)
        self.columnCountChanged.emit(columnCount)



    def setItemValue(self):
        for i in range(self.rowCount()):
            for j in range(self.columnCount()):
                item1 = QTableWidgetItem('')
                self.setItem(i, j, item1)
    def dragbottom_trig(self, items):

        first = items[0]
        v = first.text()
        item_list=items[1:]
        item_num=len(item_list)
        list1 = []
        for i in range(item_num):
            a, b, c = JudgeStrDataType(v)
            if a == 1:
                new_str1 = str(eval(v) + i+1)
                list1.append(new_str1)
            elif a == 2 or a == 4 or a == 5:
                list1.append(v)
            elif a == 3:
                replace_str = c + i+1
                new_str2=''
                for k in range(len(v)):
                    if k==v.rfind(str(c)):
                        new_str2=new_str2+str(replace_str)
                    else:
                        new_str2=new_str2+v[k]
                list1.append(new_str2)
        # print(list1)
        # 添加填充
        for one in item_list:
            pos_num=item_list.index(one)
            self.setItem(one.row(), one.column(), QTableWidgetItem(list1[pos_num]))


class Table(QWidget):

    def __init__(self):
        super().__init__()
        self.setGeometry(300, 300, 500, 400)
        self.setWindowTitle('QTableWidget的基本用法')
        layout = QHBoxLayout()
        self.table = MyTableWidget()
        # self.table.dragbottom.connect(self.dragbottom_trig)
        self.table.setRowCount(8)
        self.table.setColumnCount(6)
        layout.addWidget(self.table)
        self.setLayout(layout)

if __name__ == "__main__":
    app = QApplication(sys.argv)
    form = Table()
    form.show()
    sys.exit(app.exec_())

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值