PyQt5 – 当鼠标悬停在非可编辑组合框的行编辑部分时的背景图片

  • Post category:Python

首先,PyQt5是一个Python的GUI编程工具包,可以用于创建各种各样的桌面应用程序。其中包含的QComboBox是一个常用的组件,用于提供下拉式菜单。当然,QComboBox还有其他应用。比如,我们可以使得非可编辑组合框的行编辑部分在鼠标悬停时出现背景图片。

下面就来详细讲解如何实现该功能。

首先,我们需要在Python环境中安装PyQt5。可以通过pip工具来安装。下面是安装命令:

pip install PyQt5

接着,我们需要导入需要使用的库:

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

现在我们可以创建一个QComboBox组件,并为其添加一些选项。这里我们为了演示方便,先使用静态数据:

class MyWindow(QWidget):
    def __init__(self):
        super(MyWindow, self).__init__()

        self.combo_box = QComboBox(self)
        self.combo_box.addItem("item 1")
        self.combo_box.addItem("item 2")
        self.combo_box.addItem("item 3")
        self.combo_box.setEditable(True)

在上述代码中,我们首先创建了一个QComboBox组件。然后为其添加了三个选项。最后,我们将其设置为可编辑。

接下来,我们就可以开始实现鼠标悬停时的背景图片效果了。我们需要重载该组件的QComboBox.view()方法。

class MyComboBox(QComboBox):
    def __init__(self, parent=None):
        super(MyComboBox, self).__init__(parent)

        self.setStyleSheet("QComboBox::drop-down {width: 0;}")
        self.view().setMouseTracking(True)

    def view(self):
        '''
        重载view方法,在view被创建时设置view的MouseTracking开关
        '''
        view_w = QComboBox.view(self)
        if not hasattr(self, '_own_view'):
            self._own_view = View(self)
        return self._own_view

class View(QAbstractItemView):
    def __init__(self, parent=None):
        super(View, self).__init__(parent)
        self.setMouseTracking(True)
        self.setEditTriggers(QAbstractItemView.DoubleClicked | QAbstractItemView.SelectedClicked)

    def mouseMoveEvent(self, event):
        '''
        鼠标移动事件,获取当前高亮行,并更新该行的背景
        '''
        pos = event.pos()
        index = self.indexAt(pos)
        if index.isValid():
            self.update(index)
        QAbstractItemView.mouseMoveEvent(self, event)

    def drawBackground(self, painter, rect, index):
        '''
        重载背景绘制,实现背景图片效果
        '''
        option = QStyleOptionViewItem()
        self.initStyleOption(option, index)

        if option.state & QStyle.State_HasFocus:
            option.state ^= QStyle.State_HasFocus

        if option.state & QStyle.State_MouseOver:
            option.state ^= QStyle.State_MouseOver
            painter.fillRect(rect, QColor("#F2F2F2"))
        else:
            painter.fillRect(rect, QBrush(Qt.white))

        if index.isValid() and self.currentRow() == index.row():
            pen = QPen(QColor("#CFCFCF"))
            pen.setWidth(2)
            painter.setPen(pen)
            painter.drawLine(rect.bottomLeft(), rect.bottomRight())

        if option.state & QStyle.State_Selected:
            option.state ^= QStyle.State_Selected
        QStyleOptionViewItem.__init__(self, option)
        super(View, self).paint(painter, option, index)

    def visualRegionForSelection(self, selection):
        '''
        获取鼠标位置事件
        '''
        vrs = QRegion()
        for item in selection.indexes():
            self.update(item)
            vrs += self.visualRect(item)
        return vrs

上述代码中,我们首先重载了QComboBox组件,并将其view设置为自己创建的View组件。同时,我们重载View组件的方法,通过鼠标操作来更新列表中需要高亮的那一项,并用CSS样式设置了下拉按钮的宽度为0。

在View组件中,我们实现了鼠标移动事件,并在它移动时获取当前高亮的行,并更新该行的背景。通过画布绘制方法,可以实现该特性下的背景图片效果。

之后,我们将MyComboBox作为控件添加至窗口中,我们就可以在鼠标移动到QComboBox组件上时看到它的背景图片效果了。

下面是一个完整的示例代码:

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

class MyWindow(QWidget):
    def __init__(self):
        super(MyWindow, self).__init__()

        self.combo_box = MyComboBox(self)
        self.combo_box.addItem("item 1")
        self.combo_box.addItem("item 2")
        self.combo_box.addItem("item 3")
        self.combo_box.setEditable(True)

        layout = QHBoxLayout(self)
        layout.addWidget(self.combo_box)

class MyComboBox(QComboBox):
    def __init__(self, parent=None):
        super(MyComboBox, self).__init__(parent)

        self.setStyleSheet("QComboBox::drop-down {width: 0;}")
        self.view().setMouseTracking(True)

    def view(self):
        '''
        重载view方法,在view被创建时设置view的MouseTracking开关
        '''
        view_w = QComboBox.view(self)
        if not hasattr(self, '_own_view'):
            self._own_view = View(self)
        return self._own_view

class View(QAbstractItemView):
    def __init__(self, parent=None):
        super(View, self).__init__(parent)
        self.setMouseTracking(True)
        self.setEditTriggers(QAbstractItemView.DoubleClicked | QAbstractItemView.SelectedClicked)

    def mouseMoveEvent(self, event):
        '''
        鼠标移动事件,获取当前高亮行,并更新该行的背景
        '''
        pos = event.pos()
        index = self.indexAt(pos)
        if index.isValid():
            self.update(index)
        QAbstractItemView.mouseMoveEvent(self, event)

    def drawBackground(self, painter, rect, index):
        '''
        重载背景绘制,实现背景图片效果
        '''
        option = QStyleOptionViewItem()
        self.initStyleOption(option, index)

        if option.state & QStyle.State_HasFocus:
            option.state ^= QStyle.State_HasFocus

        if option.state & QStyle.State_MouseOver:
            option.state ^= QStyle.State_MouseOver
            painter.fillRect(rect, QColor("#F2F2F2"))
        else:
            painter.fillRect(rect, QBrush(Qt.white))

        if index.isValid() and self.currentRow() == index.row():
            pen = QPen(QColor("#CFCFCF"))
            pen.setWidth(2)
            painter.setPen(pen)
            painter.drawLine(rect.bottomLeft(), rect.bottomRight())

        if option.state & QStyle.State_Selected:
            option.state ^= QStyle.State_Selected
        QStyleOptionViewItem.__init__(self, option)
        super(View, self).paint(painter, option, index)

    def visualRegionForSelection(self, selection):
        '''
        获取鼠标位置事件
        '''
        vrs = QRegion()
        for item in selection.indexes():
            self.update(item)
            vrs += self.visualRect(item)
        return vrs

if __name__ == '__main__':
    app = QApplication([])
    window = MyWindow()
    window.show()
    app.exec_()

可以看到,运行上述代码会弹出一个窗口,其中有一个可编辑的下拉框组合框,当鼠标移动到组合框的某一行时,该行会高亮,并显示背景图片效果。

除以上静态数据,我们还可以结合数据库、网络等进行动态的数据传递,用同样的方式进行实现。

应用场景:

  1. 当需要为下拉框的选项卡提供细微变化时;
  2. 根据需要更改列表中的行颜色;
  3. 等等。

总之,基于Python的PyQt5工具包,我们可以轻松实现非可编辑组合框的行编辑部分时的背景图片效果。