PyQt5 使用自定义ToolTip解决QTableWidget数据显示不全问题

待我称王封你为后i 2023-07-24 08:56 247阅读 0赞

目录

问题描述

自定义QTableWidget

代码解释

实例演示代码

实现效果

总结

参考资料

补充更新



问题描述

最近在使用PyQT5做一个项目,用到最多的控件是QTableWidget,使用很方便,但是也存在很多问题,比如下面这种:

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2RhaW1hc2hpcmVu_size_16_color_FFFFFF_t_70

数据太长,使用QTableWidget默认的列宽分配会导致显示不全,数据以”XXX…”的形式呈现,很不利于用户阅读。一个可能的解决办法是采用用户可以手动调整的方式来显示数据。效果如下图:

图像无法显示

使用的代码是这一句:

  1. TableWidget.horizontalHeader().setSectionResizeMode(QHeaderView.Interactive) # 列宽手动调整

可以看到,貌似问题解决了,可是界面已经完全变形了,实在让人觉得不愉悦,那么有什么更好的解决方案吗?

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2RhaW1hc2hpcmVu_size_16_color_FFFFFF_t_70 1

只见柯南君思考片刻后,抬了抬那光芒四射的眼镜,说道:

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2RhaW1hc2hpcmVu_size_16_color_FFFFFF_t_70 2

hahaha看到这,要是再不把代码贴上来可能会被打了,那就话不多说,show me the code!

自定义QTableWidget

  1. # -*- coding: utf-8 -*-
  2. """
  3. @author:daimashiren
  4. @time:2020-04-15
  5. """
  6. from PyQt5.QtWidgets import *
  7. from PyQt5.QtCore import *
  8. from PyQt5.QtGui import *
  9. import traceback
  10. from functools import partial
  11. # 自定义的QTableWidget,使用ToolTip提示用户当前单元格内的详细内容
  12. class MyTableWidget(QTableWidget):
  13. update_table_tooltip_signal = pyqtSignal(object)
  14. def __init__(self, row, col):
  15. super(MyTableWidget, self).__init__()
  16. self.setRowCount(row)
  17. self.setColumnCount(col)
  18. self.ini_table()
  19. def ini_table(self):
  20. """---------初始化表格的常用选项(按需修改)------------"""
  21. QTableWidget.resizeColumnsToContents(self)
  22. QTableWidget.resizeRowsToContents(self)
  23. self.setSelectionMode(QAbstractItemView.NoSelection)
  24. self.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch) # 列宽自动分配
  25. self.verticalHeader().setSectionResizeMode(QHeaderView.Stretch) # 行高自动分配
  26. # self.verticalHeader().stretchLastSection() #自动拓展最后一行适应表格高度
  27. self.horizontalHeader().setVisible(False)
  28. self.verticalHeader().setVisible(False)
  29. self.setEditTriggers(QAbstractItemView.NoEditTriggers)
  30. """------------关键代码--------------"""
  31. self.vertical_scrollbar = QScrollBar()
  32. self.horizon_scrollbar = QScrollBar()
  33. self.vertical_scrollbar.valueChanged.connect(partial(self.scollbar_change_slot, "vertical"))
  34. self.horizon_scrollbar.valueChanged.connect(partial(self.scollbar_change_slot, "horizon"))
  35. self.setVerticalScrollBar(self.vertical_scrollbar)
  36. self.setHorizontalScrollBar(self.horizon_scrollbar)
  37. self.init_row = 0
  38. self.init_col = 0
  39. self.tool_tip = ""
  40. self.update_table_tooltip_signal.connect(self.update_table_tooltip_slot)
  41. self.title_row_height = 0
  42. # 设置表格列标题
  43. def set_horizon_title(self, title_list):
  44. self.horizontalHeader().setDefaultAlignment(Qt.AlignLeft)
  45. self.horizontalHeader().setVisible(True)
  46. col = 0
  47. for item in title_list:
  48. item = QTableWidgetItem(str(item))
  49. item.setSizeHint(QSize(200, 45)) # 这里默认设置了列标题的宽和高分别为200、45,可根据需要修改
  50. self.setHorizontalHeaderItem(col, item)
  51. col += 1
  52. self.title_row_height = 45 # (关键值)这里的值设置为列标题高
  53. # 为TableWidget安装事件过滤器
  54. def install_eventFilter(self):
  55. self.installEventFilter(self)
  56. self.setMouseTracking(True)
  57. #改变滚动条时重置当前页面的初始行和列
  58. def scollbar_change_slot(self, type):
  59. if type == "vertical":
  60. value = self.verticalScrollBar().value()
  61. self.init_row = value
  62. # print("垂直滚动条当前的值为:",value)
  63. # print("当前页面的起始行为:",self.init_row)
  64. else:
  65. value = self.horizontalScrollBar().value()
  66. self.init_col = value
  67. # print("水平滚动条当前的值为:",value)
  68. # print("当前页面的起始列为:",self.init_col)
  69. # 通过计算坐标确定当前位置所属单元格
  70. def update_table_tooltip_slot(self, posit):
  71. self.tool_tip = ""
  72. self.mouse_x = posit.x()
  73. self.mouse_y = posit.y()
  74. self.row_height = self.title_row_height # 累计行高,初始值为列标题行高
  75. for r in range(self.rowCount()):
  76. current_row_height = self.rowHeight(r)
  77. self.col_width = 0 # 累计列宽
  78. if self.row_height <= self.mouse_y <= self.row_height + current_row_height:
  79. for c in range(self.columnCount()):
  80. current_col_width = self.columnWidth(c)
  81. if self.col_width <= self.mouse_x <= self.col_width + current_col_width:
  82. r = self.init_row + r
  83. c = self.init_col + c
  84. print("鼠标当前所在的行和列为:({},{})".format(r, c))
  85. item = self.item(r, c)
  86. if item != None:
  87. self.tool_tip = item.text()
  88. else:
  89. self.tool_tip = ""
  90. return self.tool_tip
  91. else:
  92. self.col_width = self.col_width + current_col_width
  93. else:
  94. if self.mouse_y < self.row_height:
  95. break
  96. else:
  97. self.row_height = self.row_height + current_row_height
  98. # 事件过滤器
  99. def eventFilter(self, object, event):
  100. try:
  101. if event.type() == QEvent.ToolTip:
  102. self.setCursor(Qt.ArrowCursor)
  103. print("当前鼠标位置为:", event.pos())
  104. self.update_table_tooltip_signal.emit(event.pos())
  105. # 设置提示气泡显示范围矩形框,当鼠标离开该区域则ToolTip消失
  106. rect = QRect(self.mouse_x, self.mouse_y, 30, 10) # QRect(x,y,width,height)
  107. # 设置QSS样式
  108. self.setStyleSheet(
  109. """QToolTip{border:10px;
  110. border-top-left-radius:5px;
  111. border-top-right-radius:5px;
  112. border-bottom-left-radius:5px;
  113. border-bottom-right-radius:5px;
  114. background:#4F4F4F;
  115. color:#00BFFF;
  116. font-size:18px;
  117. font-family:"微软雅黑";
  118. }""")
  119. QApplication.processEvents()
  120. # 在指定位置展示ToolTip
  121. QToolTip.showText(QCursor.pos(), self.tool_tip, self, rect, 1500)
  122. """
  123. showText(QPoint, str, QWidget, QRect, int)
  124. #############参数详解###########
  125. #QPoint指定tooptip显示的绝对坐标,QCursor.pos()返回当前鼠标所在位置
  126. #str为设定的tooptip
  127. #QWidget为要展示tooltip的控件
  128. #QRect指定tooltip显示的矩形框范围,当鼠标移出该范围,tooltip隐藏,使用该参数必须指定Qwidget!
  129. #int用于指定tooltip显示的时长(毫秒)
  130. """
  131. return QWidget.eventFilter(self, object, event)
  132. except Exception as e:
  133. traceback.print_exc()

代码里的关键点我已经做了批注了,这里再总结一下吧!

代码解释

  • 创建一个eventFilter,捕获当前所有事件并从中筛选出tooltip事件和要显示tooltip的控件QTableWidget.
  • 为QTableWidget安装上述eventFilter并设置自动捕获鼠标.``

self.TableWidget.installEventFilter(self)

self.TableWidget.setMouseTracking(True)

  • 编写一个判断鼠标当前位于哪个单元格的函数并且更新toop_tip为当前单元格内的内容.
  • 最后在EventFilter里的Tooptip event下使用QToolTip.showtext()方法展示实时更新的toop_tip即可.

实例演示代码

  1. #测试自定义TableWidget
  2. class test_table_win(QWidget):
  3. def __init__(self):
  4. super(test_table_win, self).__init__(parent=None)
  5. self.init_ui()
  6. def init_ui(self):
  7. self.setWindowTitle("测试ToolTip")
  8. self.setGeometry(500,400,500,300)
  9. self.main_layout = QVBoxLayout()
  10. self.main_layout.setContentsMargins(0,0,0,0)
  11. self.setLayout(self.main_layout)
  12. """调用自定义TableWidget控件"""
  13. self.TableWidget = MyTableWidget(5, 3)
  14. self.TableWidget.resize(self.width(),225)
  15. self.TableWidget.verticalHeader().stretchLastSection()
  16. title_list = ["省份","省会","旅游景点"]
  17. self.TableWidget.set_horizon_title(title_list)
  18. """为TableWidget安装事件过滤器(关键)"""
  19. self.TableWidget.install_eventFilter()
  20. self.main_layout.addWidget(self.TableWidget)
  21. def set_table_content(self,list1,list2,list3):
  22. row = 0
  23. for item1,item2,item3 in zip(list1,list2,list3):
  24. self.TableWidget.setItem(row,0,QTableWidgetItem(item1))
  25. self.TableWidget.setItem(row,1,QTableWidgetItem(item2))
  26. self.TableWidget.setItem(row,2,QTableWidgetItem(item3))
  27. row = row+1
  28. if __name__ == "__main__":
  29. import sys
  30. province_list = ["四川", "广西", "贵州", "云南", "广东"]
  31. city_list = ["成都", "南宁", "贵阳", "昆明", "广州"]
  32. site_list = ["九寨沟、黄龙、峨眉山、青城山、乐山大佛、都江堰",
  33. "桂林漓江、钦州三娘湾、北海银滩、大新德天瀑布、百色乐业大石围天坑群",
  34. "黄果树瀑布、赤水丹霞、织金洞、红枫湖、梵净山、遵义会址",
  35. "怒江、九龙瀑布群、三江并流、西双版纳热带植物园、罗平螺丝田、玉龙雪山、丽江古城、香格里拉",
  36. "韶关丹霞山、广州塔、广州白云山、江门开坪碉楼、南澳岛、罗浮山"]
  37. app = QApplication(sys.argv)
  38. test_table = test_table_win()
  39. test_table.set_table_content(province_list,city_list,site_list)
  40. test_table.show()
  41. sys.exit(app.exec_())

实现效果

实现效果如图所示,有点类似JS做出来的悬浮窗的感觉。当鼠标移动到QTableWidget的某一单元格上时,会以ToolTip悬浮窗的形式展示该单元格内的全部内容,这样就不用担心显示不全的问题啦!

图片无法显示

总结

  1. 自定义一个QTableWidget控件(上文中的MyTableWidget)
  2. 实例化MyTableWidget,调用MyTableWidget的 install_eventFilter()方法为控件安装事件过滤器

码字不易,喜欢请点赞哟!

参考资料

https://doc.qt.io/qt-5/qtooltip.html

https://doc.qt.io/qt-5/qtablewidget.html

https://blog.csdn.net/zgrjkflmkyc/article/details/41381651

https://stackoverflow.com/questions/7829829/pyqt4-mousemove-event-without-mousepress


补充更新

官方文档真的是个好东西,最近又发现了一个更简单的办法,分享给大家,代码量可以减少一半!!!

  1. """
  2. @author: daimashiren
  3. @time: 2020/05/22
  4. """
  5. # -*- coding: utf-8 -*-
  6. from PyQt5.QtWidgets import QTableWidget,QAbstractItemView
  7. from PyQt5.QtWidgets import QApplication,QWidget,QToolTip
  8. from PyQt5.QtWidgets import QHeaderView
  9. from PyQt5.QtCore import pyqtSignal,QEvent,Qt,QRect
  10. from PyQt5.QtGui import QCursor
  11. import traceback
  12. #自定义的自带单元格提示功能的QTableWidget
  13. class MyTableWidget(QTableWidget):
  14. update_table_tooltip_signal = pyqtSignal(object)
  15. def __init__(self,row,col):
  16. super(MyTableWidget, self).__init__()
  17. self.setRowCount(row)
  18. self.setColumnCount(col)
  19. self.ini_table()
  20. def ini_table(self):
  21. """---------初始化表格的常用选项(按需修改)------------"""
  22. QTableWidget.resizeColumnsToContents(self)
  23. QTableWidget.resizeRowsToContents(self)
  24. self.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch) # 列宽自动分配
  25. self.horizontalHeader().setVisible(True)
  26. self.verticalHeader().setVisible(False)
  27. self.setEditTriggers(QAbstractItemView.NoEditTriggers)
  28. """------------关键代码--------------"""
  29. self.installEventFilter(self)
  30. self.setMouseTracking(True)
  31. #绑定槽函数
  32. self.itemEntered.connect(self.enter_item_slot)
  33. #获得鼠标进入的单元格对应的QTableWidgetItem对象
  34. def enter_item_slot(self, item):
  35. self.tool_tip = item.text()
  36. print("self.tool_tip:",self.tool_tip)
  37. def eventFilter(self, object, event):
  38. try:
  39. if event.type() == QEvent.ToolTip and self.tool_tip!= None:
  40. print("self.tool_tip:",self.tool_tip)
  41. self.setCursor(Qt.ArrowCursor)
  42. print("当前鼠标位置为:", event.pos())
  43. # 设置提示气泡显示范围矩形框,当鼠标离开该区域则ToolTip消失
  44. rect = QRect(event.pos().x(), event.pos().y(), 30, 10) # QRect(x,y,width,height)
  45. # 设置QSS样式
  46. self.setStyleSheet(
  47. """QToolTip{border:10px;
  48. border-top-left-radius:5px;
  49. border-top-right-radius:5px;
  50. border-bottom-left-radius:5px;
  51. border-bottom-right-radius:5px;
  52. background:#4F4F4F;
  53. color:#00BFFF;
  54. font-size:18px;
  55. font-family:"微软雅黑";
  56. }""")
  57. QApplication.processEvents()
  58. #在指定位置展示ToolTip
  59. QToolTip.showText(QCursor.pos(), self.tool_tip, self, rect, 1500)
  60. self.tool_tip = None #重设tool_tip
  61. """
  62. showText(QPoint, str, QWidget, QRect, int)
  63. #############参数详解###########
  64. #QPoint指定tooptip显示的绝对坐标,QCursor.pos()返回当前鼠标所在位置
  65. #str为设定的tooptip
  66. #QWidget为要展示tooltip的控件
  67. #QRect指定tooltip显示的矩形框范围,当鼠标移出该范围,tooltip隐藏,使用该参数必须指定Qwidget!
  68. #int用于指定tooltip显示的时长(毫秒)
  69. """
  70. return QWidget.eventFilter(self, object, event)
  71. except Exception as e:
  72. traceback.print_exc()

上面这个自带单元格提示功能的TableWidget的使用方法和原装的QTableWidget是一样的,只需要实例化就可以了,功能一样强大!感谢官方文档,害,早些看到就好了,颇省事!Qt官方文档的描述如下:

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2RhaW1hc2hpcmVu_size_16_color_FFFFFF_t_70 3

大意就是返回鼠标进入的单元格对应的QTableWidgetItem,我们可以利用这个内置信号获得所需的ToolTip,只需要几行代码就可以搞定。

  1. #绑定槽函数
  2. self.itemEntered.connect(self.enter_item_slot)
  3. #获得鼠标进入的单元格对应的QTableWidgetItem对象
  4. def enter_item_slot(self, item):
  5. self.tool_tip = item.text()
  6. print("self.tool_tip:",self.tool_tip)

简单省事儿,之前想的实在太复杂了(捂脸哭)。。。以后一定把官方文档研究透彻再搞代码,吸取教训了。

官方文档链接:

https://doc.qt.io/qt-5/qtablewidget.html#itemEntered

发表评论

表情:
评论列表 (有 0 条评论,247人围观)

还没有评论,来说两句吧...

相关阅读