【Python、Qt】使用QItemDelegate实现单元格的富文本显示+复选框功能

news2025/2/1 14:07:39

主打一个 折磨 坑多 陪伴。代码为Python,C++的就自己逐条语句慢慢改吧。



Python代码:

import sys
from types import MethodType
from PyQt5.QtCore import Qt,QPoint,QSize,QRect,QEvent
from PyQt5.QtGui import QStandardItemModel, QStandardItem,QTextDocument,QTextCursor
from PyQt5.QtWidgets import QTreeView,QApplication,QItemDelegate,QStyle

class RichDelegate(QItemDelegate):#使用QItemDelegate进行单元格重绘:https://blog.csdn.net/Loc_Haoclass/article/details/106528047
	__cboxSize=QSize(14,14)#复选框大小
	__cboxAlignV=Qt.AlignVCenter#竖直位置(居中)
	__cboxAlignH=Qt.AlignLeft#水平位置(左对齐)
	def __init__(self,parent=None,*,align=None,size=None):
		super().__init__(parent)
		if(align):
			self.SetCheckboxAlign(align)
		if(size):
			self.SetCheckboxSize(size)
	def SetCheckboxAlign(self,align):#设置复选框位置
		alignV=[Qt.AlignTop,Qt.AlignVCenter,Qt.AlignBottom]
		alignH=[Qt.AlignLeft,Qt.AlignHCenter,Qt.AlignRight]
		alignV=list(filter(lambda a:int(align) & int(a)!=0,alignV))
		alignH=list(filter(lambda a:int(align) & int(a)!=0,alignH))
		alignV.append(Qt.AlignVCenter)
		alignH.append(Qt.AlignLeft)
		self.__cboxAlignV=alignV[0]
		self.__cboxAlignH=alignH[0]
	def SetCheckboxSize(self,size):#设置复选框大小
		self.__cboxSize=size

	def editorEvent(self,event,model,opt,index):#处理复选框点击逻辑:https://blog.csdn.net/xbnlkdbxl/article/details/51316424
		if(event.type()==QEvent.MouseButtonRelease):#仅处理鼠标抬起事件
			if(event.button()==Qt.LeftButton):#仅处理鼠标左键
				item=index.model().itemFromIndex(index)
				if(item.isCheckable()):#仅处理复选框存在的情况
					rect_cbox=self.__GetRect_Checkbox(opt.rect)
					if(rect_cbox.contains(event.pos())):#仅复选框被点击时翻转状态
						item.setCheckState(Qt.Unchecked if item.checkState()==Qt.Checked else Qt.Checked)
						return True
		return False
	def drawCheck(self,ptr,opt,rect,state):#绘制复选框(这里直接默认绘制,有想法的可以改成其他绘制例如画个圈之类的
		super().drawCheck(ptr,opt,rect,state)#默认绘制的复选框总是正方形
	def paint(self,ptr,opt,index):
		style=opt.widget.style() if opt.widget else QApplication.style()
		style.drawControl(QStyle.CE_ItemViewItem, opt, ptr, opt.widget)#这条语句解决了行选中时背景色不变化的问题:https://blog.csdn.net/gongjianbo1992/article/details/108687172

		rect=QRect(opt.rect)
		item=index.model().itemFromIndex(index)
		if(item.isCheckable()):#绘制复选框:https://blog.csdn.net/xbnlkdbxl/article/details/51316424
			rect_cbox=self.__GetRect_Checkbox(rect)
			self.drawCheck(ptr,opt,rect_cbox,item.checkState())
			#计算剩余位置用于绘制文本内容
			if(self.__cboxAlignH==Qt.AlignRight):#只调整水平位置(应该不会有人那么异端把复选框放在单元格正中间的吧,不会吧不会吧
				rect.setRight(rect.right()-rect_cbox.width())
			else:
				rect.setLeft(rect.left()+rect_cbox.width())

		tx=index.data()
		doc=QTextDocument()
		doc.setHtml(tx)
		txDot='...'#替换为省略号
		testPoint=QPoint(rect.width(), rect.height() / 2)#获取能完整显示的字符个数:https://blog.csdn.net/eiilpux17/article/details/118461445
		pos = doc.documentLayout().hitTest(testPoint, Qt.ExactHit)
		if(pos!=-1 and pos !=len(doc.toPlainText())):#不能完全显示的情况下进行字符替换
			docDot=QTextDocument()
			docDot.setHtml(txDot)
			docDot.setDocumentMargin(0)#发现调用该语句后doc.size取值恢复正常:https://cloud.tencent.com/developer/ask/sof/105271901
			testPoint=QPoint(rect.width()-docDot.size().width(), rect.height() / 2)
			pos = doc.documentLayout().hitTest(testPoint, Qt.ExactHit)
			if(pos==-1):
				pos=0
			cursor=QTextCursor(doc)
			cursor.setPosition(pos)
			# cursor.insertText(txDot)
			cursor.movePosition(QTextCursor.End, QTextCursor.KeepAnchor)
			cursor.insertText(txDot,cursor.block().charFormat())
		LT=rect.bottomLeft()
		LT.setY(LT.y()-doc.size().height())
		# LT=rect.topLeft()
		ptr.save()
		ptr.translate(LT)
		ptr.setClipRect(rect.translated(-rect.topLeft()))
		# doc.setDefaultTextOption(doc.defaultTextOption())
		doc.drawContents(ptr)
		ptr.restore()

	def sizeHint(self,opt,index):#设置行高函数:https://blog.csdn.net/Lutx/article/details/6641353
		tx=index.data()
		doc=QTextDocument()
		doc.setHtml(tx)
		size=doc.size()
		return QSize(size.width(),size.height())

	def __GetRect_Checkbox(self,rect):#返回复选框确切位置
		alignV=[Qt.AlignTop,Qt.AlignVCenter,Qt.AlignBottom]
		alignH=[Qt.AlignLeft,Qt.AlignHCenter,Qt.AlignRight]
		posV=[rect.top(),rect.bottom()]
		posH=[rect.left(),rect.right()]
		for nape in [[self.__cboxAlignV,alignV,posV,self.__cboxSize.height()],
					[self.__cboxAlignH,alignH,posH,self.__cboxSize.width()]]:
			align,alignLst,pos,width=nape
			index=alignLst.index(align)
			if(index==0):#靠左/靠上
				pos[1]=pos[0]+width
			elif(index==1):#居中
				pos[0]=pos[0]+int((pos[1]-pos[0]-width)/2)
				pos[1]=pos[0]+width
			elif(index==2):#靠右/靠下
				pos[0]=pos[1]-width
		return QRect(posH[0],posV[0],posH[1]-posH[0],posV[1]-posV[0])

if __name__ == '__main__':
	app = QApplication(sys.argv)

	tv=QTreeView()
	tv.setModel(QStandardItemModel(tv))
	model=tv.model()
	model.appendRow([QStandardItem(d) for d in ['<font color="red" size=3> R3 </font>']])
	model.appendRow([QStandardItem(d) for d in ['<font color="red" size=5> R5 </font>']])
	model.appendRow([QStandardItem(d) for d in ['<font color="red" size=7> R7 </font>']])
	model.appendRow([QStandardItem(d) for d in ['<font color="red" style="font-size:50px"> R50px </font>','<font style="background:#0000FF;font-size:70px">B70px</font>']])
	model.appendRow([QStandardItem(d) for d in ['<font size=5><sub>bbb</sub><sup>ppp</sup><br><s>SSS</s><i>III</i><u>UUU</u></font><br><font color="red" style="background:#00FFFF;font-size:20pt">R20pt</font>']])

	model.item(1,0).setCheckable(True)
	model.item(3,1).setCheckable(True)
	model.item(4,0).setCheckable(True)
	rich_1=RichDelegate()
	rich_2=RichDelegate(align=Qt.AlignBottom|Qt.AlignRight)#复选框右对齐是什么邪道行为,太怪了(感觉除了左居中以外的对齐都是邪道
	tv.setItemDelegateForRow(0,rich_1)
	tv.setItemDelegateForRow(1,rich_1)
	tv.setItemDelegateForRow(2,rich_1)
	tv.setItemDelegateForRow(3,rich_1)
	tv.setItemDelegateForRow(4,rich_2)
	tv.show()
	sys.exit(app.exec())

运行结果:

运行结果


补充:

1、我的代码仅完成富文本显示,像是往单元格里塞入按钮、下拉列表亦或是其他控件不在本篇讨论范围之内,有需要的可以参考[CSDN]QStyledItemDelegate单元格数据渲染与编辑、[51CTO]QTableWidget使用setCellWidget设置控件居中显示或是自行搜索其他文章
2、复选框的绘制样式甚至可以自定义,像是画成圆圈或是其他东西,又或是嫌黑色不好看改成紫色绿色啥的,只不过得自己实现就是了,重绘仅需QPainter倒少了挺多麻烦(只不过还是挺麻烦的所以没这需求就没必要自找麻烦


参考资料:

  • 使用QItemDelegate显示富文本:[CSDN]https://blog.csdn.net/Loc_Haoclass/article/details/106528047
  • QItemDelegate实现CheckBox复选框功能:[CSDN]https://blog.csdn.net/xbnlkdbxl/article/details/51316424
  • QStyledItemDelegate单元格数据渲染与编辑:[CSDN]https://blog.csdn.net/gongjianbo1992/article/details/108687172
  • 精确获取字符完整显示个数:[CSDN]https://blog.csdn.net/eiilpux17/article/details/118461445
  • QItemDelegate单元格设置行高:[CSDN]https://blog.csdn.net/Lutx/article/details/6641353

本文发布于CSDN,未经个人同意不得私自转载:https://blog.csdn.net/weixin_44733774/article/details/133838003

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1093826.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

LeetCode - 318 最大单词长度乘积(Java JS Py C)

目录 题目来源 题目描述 示例 提示 题目解析 算法源码 题目来源 318. 最大单词长度乘积 - 力扣&#xff08;LeetCode&#xff09; 题目描述 给你一个字符串数组 words &#xff0c;找出并返回 length(words[i]) * length(words[j]) 的最大值&#xff0c;并且这两个单词…

关于vue3启动的一些错误总结

一、成功解决 npm ERR! ERESOLVE could not resolve 解决办法&#xff1a; npm i --legacy-peer-deps –legacy-peer-deps 作用&#xff1a; 在NPM v7中&#xff0c;现在默认安装peerDependencies。 在很多情况下&#xff0c;这会导致版本冲突&#xff0c;从而中断安装过程。 …

【Transformer系列】关于Transformer的若干问题FAQ解析

一、参考资料 Transformer的细节到底是怎么样的&#xff1f;Transformer 18问 答案解析(1)—史上最全Transformer面试题&#xff1a;灵魂20问帮你彻底搞定Transformer 关于Transformer的若干问题整理记录 Transformer的细节与技巧 二、FAQ Q&#xff1a;什么是Transformer&…

ubuntu修改IP地址

参考&#xff1a;ubuntu修改配置IP地址和DNS的方法总结&#xff08;4种&#xff09;_ubuntu设置ip地址-CSDN博客 面对ubuntu18以上的版本&#xff0c;主要有两种界面&#xff1a;图形化界面和纯命令行界面。 图形化界面配置比较简单&#xff0c;命令行配置稍许复杂&#xff0c…

python中的logging的使用

初级应用 import logginglogging.basicConfig(levellogging.INFO,format%(asctime)s| %(filename)s[line:%(lineno)s] |%(levelname)s| %(message)s,datefmt%Y-%m-%d %H:%M:%S,filenamesys.path[0]/running_log.log,filemodea) logging.info(This is log)高级应用 # logging的…

HTTP 响应头 X-Frame-Options

简介 X-Frame-Options HTTP 响应头用来给浏览器一个指示。该指示的作用为&#xff1a;是否允许页面在 <frame>, </iframe> 或者 <object> 中展现。 网站可以使用此功能&#xff0c;来确保自己网站的内容没有被嵌套到别人的网站中去&#xff0c;也从而避免了…

chatGPT 帮我优化mysql查询语句 优化一下查询速度

最终效果 备份一下数据库&#xff0c;加上索引就行。25ms查询完成。 对比 加上索引之前及之后的 EXPLAIN SELECT d.sn, d.imei1, d.imei2, d.remark FROM device_info_new d INNER JOIN production_log p ON d.state ‘0’ AND p.sn d.sn AND p.imei1 d.imei1 AND p.imei2 …

在雷电模拟器9上安装magisk并安装LSPosed模块以及其Manager管理器(一)

环境&#xff1a;win10 64&#xff0c;雷电模拟器9.0.60(9)&#xff0c;Android 9。 之前我都是用雷电模拟器版本4.0.78&#xff0c;Android版本7.1.2&#xff0c;为什么本篇要使用9了呢&#xff1f;先解答下这个问题。原因如下&#xff1a;经过我的测试&#xff0c;LSPosed不支…

【递归】汉诺塔问题(Java版)

目录 1.题目解析 2.讲解算法原理 2.1.如何来解决汉诺塔问题&#xff1f; 2.2.为什么这道题可以用递归来做&#xff1f; 2.2.1 什么是递归 2.2.2 为什么会用到递归 3.如何编写递归代码&#xff1f; 4.递归的细节展开图 1.题目解析 汉诺塔问题链接 在经典汉诺塔问题中&a…

激活函数小结:ReLU、ELU、Swish、GELU等

文章目录 SigmoidTanhReLULeaky ReLUPReLUELUSoftPlus MaxoutMishSwishGELUSwiGLUGEGLU资源 激活函数是神经网络中的非线性函数&#xff0c;为了增强网络的表示能力和学习能力&#xff0c;激活函数有以下几点性质&#xff1a; 连续且可导&#xff08;允许少数点上不可导&#x…

halcon深度学习中的样本增强

一、问题描述 halcon的例程中&#xff0c;下面是最经典的语义分割例子。 但是&#xff0c;它并没做样本增强&#xff0c;因为 你看下图的代码&#xff0c;第90行&#xff0c;那两个参数都是[]&#xff0c;空的。 二、解决方案 如下图所示&#xff0c;增加了从95到108行&#…

启航kp OpenHarmony环境搭建

前提 启航kp OpenHarmony环境搭建 搭建好OpenHarmony环境 未搭建好可以参考OpenHarmony docker环境搭建 安装vscode 下载好启航kp所需的开发包和样例 下载地址 搭建过程 进入正确文件夹 首先要进入 /home/openharmony 目录下&#xff0c;如果没有打开在vsc左上角找到文…

DELL R710硬盘格式化方法备忘录

DELL R710硬盘格式化方法备忘录 本方法是全部格式化硬盘&#xff0c;不能格式化其中的某一块 开机出现带CTRLR字样后 按CTRLR 按下CTRLP三次 以上是目前硬盘的状态&#xff0c;使用中的是ONLINE状态&#xff0c; 按CTRLP&#xff0c;回到第一个界面&#xff0c;然后按F2 选择…

【线上Java项目部署Bug记录】天翼云80端口不能使用,即使暴露了也不行!!!

项目场景&#xff1a; 项目背景&#xff1a;使用Docker部署SpringBoot项目&#xff0c;前端是 Vue 项目是&#xff1a;https://gitee.com/JavaLionLi/RuoYi-Vue-Plus 服务器&#xff1a;天翼云服务器 端口&#xff1a; 问题描述 所有端口都开的好好的&#xff0c;docker的…

山西电力市场日前价格预测【2023-10-15】

日前价格预测 预测说明&#xff1a; 如上图所示&#xff0c;预测明日&#xff08;2023-10-15&#xff09;山西电力市场全天平均日前电价为409.82元/MWh。其中&#xff0c;最高日前价格为722.95元/MWh&#xff0c;预计出现在19: 00。最低日前电价为255.87元/MWh&#xff0c;预计…

tkinter自定义组件:文件选择按钮和颜色选择按钮

文章目录 文件对话框按钮代码实现颜色对话框 tkinter系列&#xff1a; GUI初步&#x1f48e;布局&#x1f48e;绑定变量&#x1f48e;绑定事件&#x1f48e;消息框&#x1f48e;文件对话框Frame控件&#x1f48e;PanedWindow和notebook控件扫雷小游戏&#x1f48e;强行表白神器…

人工智能就业前景越来越严峻了,你还在坚持吗?

点击上方关注 “终端研发部” 设为“星标”&#xff0c;和你一起掌握更多数据库知识 从最近最火的chatGpt来看&#xff0c;AI时长不但没有低迷下去&#xff0c;而且还越来越好了&#xff01;去年毕业的一个朋友&#xff0c;硕士毕业&#xff0c;目前在字节做机器学习工程师&…

【Spring框架】Spring监听器的简介和基本使用

目录 一、观察者模式 1.1 模型介绍 1.2 观察者模式Demo 1.2.1 观察者实体 1.2.2 主题实体 1.2.3 测试代码 二、Spring监听器的介绍 2.1 事件&#xff08;ApplicationEvent&#xff09; 2.1.1 Spring内置事件 2.1.2 Spring内置事件 2.2 事件监听器&#xff08;Applic…

阿里云安全中心需要购买吗?功能及价格告诉你值不值!

阿里云云安全中心有必要购买吗&#xff1f;云安全中心经常提示云服务器高危漏洞&#xff0c;需要购买云安全中心吗&#xff1f;无论是云服务器上是网站还是其他应用&#xff0c;难免会存在漏洞&#xff0c;有漏洞是一定要修复的&#xff0c;云安全中心不仅可以修复漏洞还可以防…