一、学习笔记
(一)、导入模板及设置界面
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_())