如何为每个QTableView单元格支持两个单独的双击值?

aknuds1

使用PyQt5,我需要在QTableView中的每个单元格中显示两个值。基本上,每一列都必须分为两个逻辑子列。将鼠标指针悬停在一个值上方时,应突出显示其文本,但不要突出显示同一单元格中的其他值。类似地,应该可以对单元格中各个值的双击做出反应。我该如何实施?

aknuds1

我通过在QTableView上实现一个细微的变化解决了该问题,该变化使用QStyledItemDelegate子类来绘制两个不同的值(突出显示或不突出显示),并检测何时双击它们。请注意,在模型中,每个像元的两个值表示为以分号分隔的字符串。

屏幕截图

从该屏幕快照中可以看到,左上角的左值被突出显示(由于将鼠标悬停在其上方)。

屏幕截图

代码

该代码由三个主要部分组成:表格视图(QTableView的子类),委托(QStyledItemDelegate的子类)和使用表格视图的应用程序代码。

表格检视

import sys
from PyQt5 import QtWidgets, QtGui, QtCore


class TableView(QtWidgets.QTableView):
    def __init__(self, parent):
        super(TableView, self).__init__(parent)
        self.__pressed_index = None
        self.__entered_index = None
        self.setItemDelegate(SplitCellDelegate(self))
        self.setEditTriggers(QtWidgets.QAbstractItemView.NoEditTriggers)
        for header in (self.horizontalHeader(), self.verticalHeader()):
            header.installEventFilter(self)

    def mouseDoubleClickEvent(self, event):
        super(TableView, self).mouseDoubleClickEvent(event)

        index = self.indexAt(event.pos())
        if not index.isValid() or not self.__is_index_enabled(index) or self.__pressed_index != index:
            me = QtGui.QMouseEvent(QtCore.QEvent.MouseButtonPress, event.localPos(), event.windowPos(), event.screenPos(),
                event.button(), event.buttons(), event.modifiers())
            return

        index_rel_pos = self.__get_index_rel_pos(event, index)
        delegate = self.itemDelegate(index)
        delegate.double_clicked(index, index_rel_pos)

    def mousePressEvent(self, event):
        super(TableView, self).mousePressEvent(event)
        self.__pressed_index = self.indexAt(event.pos())

    def mouseMoveEvent(self, event):
        super(TableView, self).mouseMoveEvent(event)
        if self.state() == self.ExpandingState or self.state() == self.CollapsingState or self.state() == self.DraggingState:
            return

        index = self.indexAt(event.pos())

        if self.__entered_index is not None and index != self.__entered_index:
            # We've left the currently entered index
            self.itemDelegate(self.__entered_index).left(self.__entered_index) 
            self.__entered_index = None

        if not index.isValid() or not self.__is_index_enabled(index):
            # No index is currently hovered above
            return

        self.__entered_index = index
        index_rel_pos = self.__get_index_rel_pos(event, index)
        self.itemDelegate(index).mouse_move(index, index_rel_pos)

    def leaveEvent(self, event):
        super(TableView, self).leaveEvent(event)

        self.__handle_mouse_exit()

    def __handle_mouse_exit(self):
        if self.__entered_index is None:
            return

        self.itemDelegate(self.__entered_index).left(self.__entered_index)
        self.__entered_index = None

    def eventFilter(self, obj, event):
        if (obj is not self.horizontalHeader() and obj is not self.verticalHeader()) or \
            event.type() not in (QtCore.QEvent.Enter,):
            return super(TableView, self).eventFilter(obj, event)

        self.__handle_mouse_exit()
        return False

    def __get_index_rel_pos(self, event, index):
        """Get position relative to index."""
        # Get index' y offset
        pos = event.pos()
        x = pos.x()
        y = pos.y()
        while self.indexAt(QtCore.QPoint(x, y-1)) == index:
            y -= 1
        while self.indexAt(QtCore.QPoint(x-1, y)) == index:
            x -= 1

        return QtCore.QPoint(pos.x()-x, pos.y()-y)

    def __is_index_enabled(self, index):
        return index.row() >= 0 and index.column() >= 0 and index.model()

项目委托

class SplitCellDelegate(QtWidgets.QStyledItemDelegate):
    def __init__(self, parent):
        super(SplitCellDelegate, self).__init__(parent)

        self.__view = parent
        parent.setMouseTracking(True)

        self.__hor_padding = 10
        self.__above_value1  = self.__above_value2 = None
        self.__rect = None

    def paint(self, painter, option, index):
        #print('Painting; width: {}'.format(option.rect.width()))
        painter.setRenderHint(QtGui.QPainter.Antialiasing)
        #print('Painting {},{}'.format(index.row(), index.column()))

        rect = option.rect
        # Copy the rect in case it changes
        self.__rect = QtCore.QRect(option.rect)

        if option.state & QtWidgets.QStyle.State_Selected:
            painter.fillRect(rect, option.palette.highlight())

        value1, value2 = self.__split_text(index)
        value1_start, separator_start, value2_start = [x + rect.x() for x in self.__compute_offsets(index)]

        if self.__above_value1 == index:
            self.__set_bold_font(painter)
            #print('Drawing value1 highlighted')
        #print('Drawing \'{}\' from {} to {}'.format(self.__value1, value1_start, separator_start))
        text_rect = QtCore.QRectF(0, rect.y(), rect.width(), rect.height())
        painter.drawText(text_rect.translated(value1_start, 0), value1, QtGui.QTextOption(QtCore.Qt.AlignVCenter))
        if self.__above_value1 == index:
            painter.restore()
        painter.drawText(text_rect.translated(separator_start, 0), '|', QtGui.QTextOption(QtCore.Qt.AlignVCenter))
        if self.__above_value2 == index:
            self.__set_bold_font(painter)
            #print('Drawing value2 highlighted')
        #else:
            #print('Not drawing highlighted')
        painter.drawText(text_rect.translated(value2_start, 0), value2, QtGui.QTextOption(QtCore.Qt.AlignVCenter))
        if self.__above_value2 == index:
            painter.restore()

    def sizeHint(self, option, index):
        value1, value2 = self.__split_text(index)
        font = QtGui.QFont(self.__view.font())
        font.setBold(True)
        fm = QtGui.QFontMetrics(font)
        return QtCore.QSize(self.__hor_padding*2 + fm.width('{}|{}'.format(value1, value2)),
            15*2 + fm.height())

    @staticmethod
    def __set_bold_font(painter):
        painter.save()
        font = QtGui.QFont(painter.font())
        font.setBold(True)
        painter.setFont(font)

    @staticmethod
    def __split_text(index):
        text = index.data(QtCore.Qt.DisplayRole).split(';')
        value1 = text[0] + ' '
        value2 = ' ' + text[1]
        return value1, value2

    def mouse_move(self, index, pos):
        if self.__rect is None:
            return

        value1_start, separator_start, value2_start = self.__compute_offsets(index)
        x = pos.x()
        #print('Mouse move in cell: {} ({} | {})'.format(x, separator_start, value2_start))
        if x < separator_start:
            if self.__above_value1 == index:
                return
            self.__above_value1 = index
            self.__above_value2 = None
            #print('Above value1')
            self.__repaint()
        elif x >= value2_start:
            if self.__above_value2 == index:
                return
            self.__above_value2 = index
            self.__above_value1 = None
            #print('Above value2')
            self.__repaint()
        elif self.__above_value1 is not None or self.__above_value2 is not None:
            self.__above_value1 = self.__above_value2 = None
            #print('Above separator')
            self.__repaint()

    def left(self, index):
        #print('Index {},{} left'.format(index.row(), index.column()))
        self.__above_value1 = self.__above_value2 = None
        self.__repaint()

    def double_clicked(self, index, pos):
        x = pos.x()
        value1_start, separator_start, value2_start = self.__compute_offsets(index)
        if x < separator_start:
            print('Index {},{} double-clicked at value 1'.format(index.row(), index.column()))
        elif x >= value2_start:
            print('Index {},{} double-clicked at value 2'.format(index.row(), index.column()))

    def __compute_offsets(self, index):
        rect = self.__rect
        value1, value2 = self.__split_text(index)
        #print('Computing offsets; width: {}'.format(rect.width()))
        font = QtGui.QFont(self.__view.font())
        font.setBold(True)
        fm = QtGui.QFontMetrics(font)
        value2_start = rect.width() - fm.width(value2) - self.__hor_padding
        separator_start = value2_start - fm.width('|')
        value1_start = separator_start - fm.width(value1)
        #print('Offsets for {},{} are {}, {}, {}'.format(index.row(), index.column(), value1_start, separator_start, value2_start))
        return value1_start, separator_start, value2_start

    def __repaint(self):
        # TODO: Repaint only cell in question
        self.__view.viewport().repaint() 

应用程式码

class Window(QtWidgets.QMainWindow):
    def __init__(self):
        super(Window, self).__init__()

        table_view = self.__set_up_table()

        w = QtWidgets.QWidget()
        vbox = QtWidgets.QVBoxLayout(w)
        vbox.addWidget(table_view)
        self.setCentralWidget(w)

    def __set_up_table(self):
        rows = 4
        cols = 4
        table = QtGui.QStandardItemModel()
        for row in range(rows):
            l = [QtGui.QStandardItem('Row {};Column {}'.format(row, col)) for col in range(cols)]
            table.appendRow(l)
            table.setVerticalHeaderItem(row, QtGui.QStandardItem('Row {}'.format(row)))
        for col in range(cols):
            table.setHorizontalHeaderItem(col, QtGui.QStandardItem('Column {}'.format(col)))

        table_view = TableView(self)
        table_view.setModel(table)
        table_view.setSortingEnabled(True)
        table_view.resizeColumnsToContents()
        return table_view


app = QtWidgets.QApplication(sys.argv)
w = Window()
w.show()
app.exec_()

本文收集自互联网,转载请注明来源。

如有侵权,请联系[email protected] 删除。

编辑于
0

我来说两句

0条评论
登录后参与评论

相关文章

来自分类Dev

QTableView并双击一个单元格

来自分类Dev

QTableView并双击一个单元格

来自分类Dev

如何搜索两个具有两个不同值的连续单元格?

来自分类Dev

excel如何将两个单独的单元格与逗号分隔的数据进行配对?

来自分类Dev

合并两个excel表导入的单元格以匹配每个单元格

来自分类Dev

两个形状相同的数据框:如何获取每个单元格最小的数据框

来自分类Dev

合并打印中的两个单元格值

来自分类Dev

比较两个单元格中的值

来自分类Dev

如何在MATLAB中将一个单元格元素拆分为两个单独的单元格元素

来自分类Dev

如果值与来自两个单独列的条件匹配,则在一列中对单元格求和

来自分类Dev

在每个单元格上显示JSON值后,在最后一个单元格中的UITableViewCell中动态添加两个Button

来自分类Dev

如何基于两个单元格值进行搜索

来自分类Dev

如何在excel中输出彼此相邻的两个单元格的值?

来自分类Dev

如何从两个熊猫数据框中加入单元格值?

来自分类Dev

如何使用VBA将来自两个单独数据表的单元格与特定请求结合

来自分类Dev

如何在 Excel 中的两个单独列中匹配返回相关单元格字段?

来自分类Dev

集合视图内的两个定向单元格,如何获取单元格标题?

来自分类Dev

如果一行中的两个单元格都相等则如何为行着色

来自分类Dev

用公式计算两个单元格,每个单元格都包含一个公式

来自分类Dev

对每个结果+格式将两个单元格排序并显示在一个单元格中

来自分类Dev

是否可以基于包含两个单独数字的单个单元格创建图表?

来自分类Dev

如何根据Google表格中其他两个单元格的比较,使一个单元格返回值列表之一?

来自分类Dev

Excel VBA-如何基于两个单元格值添加行数并从第三个单元格复制/保留数据?

来自分类Dev

如果两个单元格匹配,则从第三个单元格返回值

来自分类Dev

从QTableView的每个单元格获取数据

来自分类Dev

在 Excel 中,如何在两个单独的工作表中匹配两个不同的值后有条件地设置单元格格式?

来自分类Dev

如何将两个单元格值的文本合并为一个| EXCEL | VBA |

来自分类Dev

如何将下两个单元格的值获取到在组合框中选择的当前选定单元格?

来自分类Dev

在UICollectionView中的每个部分显示两个单元格

Related 相关文章

  1. 1

    QTableView并双击一个单元格

  2. 2

    QTableView并双击一个单元格

  3. 3

    如何搜索两个具有两个不同值的连续单元格?

  4. 4

    excel如何将两个单独的单元格与逗号分隔的数据进行配对?

  5. 5

    合并两个excel表导入的单元格以匹配每个单元格

  6. 6

    两个形状相同的数据框:如何获取每个单元格最小的数据框

  7. 7

    合并打印中的两个单元格值

  8. 8

    比较两个单元格中的值

  9. 9

    如何在MATLAB中将一个单元格元素拆分为两个单独的单元格元素

  10. 10

    如果值与来自两个单独列的条件匹配,则在一列中对单元格求和

  11. 11

    在每个单元格上显示JSON值后,在最后一个单元格中的UITableViewCell中动态添加两个Button

  12. 12

    如何基于两个单元格值进行搜索

  13. 13

    如何在excel中输出彼此相邻的两个单元格的值?

  14. 14

    如何从两个熊猫数据框中加入单元格值?

  15. 15

    如何使用VBA将来自两个单独数据表的单元格与特定请求结合

  16. 16

    如何在 Excel 中的两个单独列中匹配返回相关单元格字段?

  17. 17

    集合视图内的两个定向单元格,如何获取单元格标题?

  18. 18

    如果一行中的两个单元格都相等则如何为行着色

  19. 19

    用公式计算两个单元格,每个单元格都包含一个公式

  20. 20

    对每个结果+格式将两个单元格排序并显示在一个单元格中

  21. 21

    是否可以基于包含两个单独数字的单个单元格创建图表?

  22. 22

    如何根据Google表格中其他两个单元格的比较,使一个单元格返回值列表之一?

  23. 23

    Excel VBA-如何基于两个单元格值添加行数并从第三个单元格复制/保留数据?

  24. 24

    如果两个单元格匹配,则从第三个单元格返回值

  25. 25

    从QTableView的每个单元格获取数据

  26. 26

    在 Excel 中,如何在两个单独的工作表中匹配两个不同的值后有条件地设置单元格格式?

  27. 27

    如何将两个单元格值的文本合并为一个| EXCEL | VBA |

  28. 28

    如何将下两个单元格的值获取到在组合框中选择的当前选定单元格?

  29. 29

    在UICollectionView中的每个部分显示两个单元格

热门标签

归档