PyQt5 自定义颜色、形状粗细、虚线等组合下拉框

目录

1、前言

1.1作者的牢骚:

1.2额外说明:

1.3Demo

2、构建思路

3、开整!部分功能说明!

3.1颜色块搭建:

4、完整代码

5、结束语


 

1、前言

1.1作者的牢骚:

搜到的资源比较少,特别是关于PyQt5,少之又少。另外资料很多都很零散,没有完整的功能制作。为此,制作了一个类似功能的下拉框,其中饶了很多弯路,也借鉴了一些前人C++制作的轮子的代码,在此基础上加上自己的理解,完成了此组合下拉框,其中不乏还有些不完美的地方,也算是抛砖引玉,希望能得到高手的指点。

1.2额外说明:

本文利用的主要是QWdiget、QToolButton、QMenu、QAction、QLayout、QPainter等,主要用QtDesigner拖拽控件和代码写控件类混合方法制作组合下拉框,包括重写控件类,然后装入QtDesigner拖拽好的QGridLayout中,也可以QtDesigner拖拽好控件,然后重写类后把控件传入类中,相当改写控件的一些功能。最后,当然读者可以改成纯代码流。

1.3Demo

2、构建思路

1、利用QToolButton和QMenu的菜单功能,将自定义的QWidget封装进QMenu,然后设置QToolButton点击时弹出自定义好的QWidget窗口即可;

2、自定义的QWidget内容设计,则参考PPT内容,包括一些标题Label,颜色块Action,图标Icon,注释文字Text、子菜单Menu、分割线Separator等。

PyQt5 自定义颜色、形状粗细、虚线等组合下拉框_第1张图片

 3、框架搭建好了,然后构思弹出菜单窗口的设计,这里我用的是Qwidget封装部件的方法构建。

首先,我们部件有标题“QLabel”、颜色块“QAction”、图标”QICon“、文字”setText“、菜单”QMenu“、分割线”QFrame“等现成的轮子和方法,因此可以多灵活运用即可。然后,利用QAction的设置图标功能,把图标设置为”带有颜色的矩形“即可,在整个QWidget除了封装QAction,我们还需要嵌套QToolButton达到弹出次级菜单的功能。最后,注意本体按钮”形状轮廓“左边的图标颜色是会随着我们选取的颜色而改变,这个自定义QIcon函数,然后运用动态设置图标即可。

PyQt5 自定义颜色、形状粗细、虚线等组合下拉框_第2张图片

3、开整!部分功能说明!

3.1颜色块搭建:

首先创建需要的颜色列表,分别有两组:【主题颜色】为6×10的二维数组,【标准颜色】为1×10的一维数组,这个我们先定义好。

        theme_colors = [
            [QColor(255, 255, 255, 255), QColor(0, 0, 0, 255), QColor(231, 230, 230, 255), QColor(64, 84, 106, 255),
             QColor(91, 155, 213, 255), QColor(237, 124, 48, 255), QColor(165, 165, 165, 255), QColor(255, 192, 0, 255),
             QColor(68, 114, 196, 255), QColor(112, 173, 71, 255)],

            [QColor(242, 242, 242, 255), QColor(127, 127, 127, 255), QColor(208, 206, 206, 255),
             QColor(214, 220, 228, 255),
             QColor(222, 235, 246, 255), QColor(251, 229, 213, 255), QColor(237, 237, 237, 237),
             QColor(255, 242, 204, 255), QColor(217, 226, 243, 255), QColor(226, 239, 217, 255)],

            [QColor(216, 216, 216, 255), QColor(89, 89, 89, 255), QColor(174, 171, 171, 255),
             QColor(173, 185, 202, 255),
             QColor(189, 215, 238, 255), QColor(247, 203, 172, 255), QColor(219, 219, 219, 255),
             QColor(254, 229, 153, 255), QColor(180, 198, 231, 255), QColor(197, 224, 179, 255)],

            [QColor(191, 191, 191, 255), QColor(63, 63, 63, 255), QColor(117, 112, 112, 255),
             QColor(132, 150, 176, 255),
             QColor(156, 195, 229, 255), QColor(244, 177, 131, 255), QColor(201, 201, 201, 255),
             QColor(255, 217, 101, 255), QColor(142, 170, 219, 255), QColor(168, 208, 141, 255)],

            [QColor(165, 165, 165, 255), QColor(38, 38, 38, 255), QColor(58, 56, 56, 255), QColor(50, 63, 79, 255),
             QColor(39, 112, 179, 255), QColor(197, 90, 17, 255), QColor(123, 123, 123, 255), QColor(191, 144, 0, 255),
             QColor(47, 84, 150, 255), QColor(83, 129, 53, 255)],

            [QColor(124, 124, 124, 255), QColor(12, 12, 12, 255), QColor(23, 22, 22, 255), QColor(34, 42, 53, 255),
             QColor(34, 81, 123, 255), QColor(124, 48, 2, 255), QColor(82, 82, 82, 255), QColor(127, 96, 0, 255),
             QColor(31, 56, 100, 255), QColor(55, 86, 35, 255)]
        ]

        # 设置基础颜色,大小是1*10一维列表
        basic_colors = [
            QColor(192, 0, 0, 255), QColor(255, 0, 0, 255), QColor(255, 192, 0, 255),
            QColor(255, 255, 0, 255), QColor(146, 208, 80, 255), QColor(0, 176, 80, 255),
            QColor(0, 176, 240, 255), QColor(0, 112, 192, 255), QColor(0, 32, 96, 255),
            QColor(112, 48, 160, 255)
        ]

‘然后,利用for循环设置QAction图标和setData让它们存储上述这些颜色数据,以待后续需要回调用到。特别地,这里用一些QLayout进行颜色块的封装和布局。同时,这些QAction点击(triggered)之后的槽函数也要规划。

        # 设置颜色6*10大小的主题颜色框架,利用QGridLayout布局放置颜色块
        pGridLayout = QGridLayout()
        pGridLayout.setAlignment(Qt.AlignCenter)
        pGridLayout.setContentsMargins(0, 0, 0, 0)
        pGridLayout.setSpacing(2)
        for i in range(6):
            for j in range(10):
                action = QAction()
                action.setData(theme_colors[i][j])
                action.setIcon(self.createColorIcon(theme_colors[i][j]))
                pBtnColor = QToolButton()
                pBtnColor.setFixedSize(QSize(20, 20))
                pBtnColor.setAutoRaise(True)
                pBtnColor.setDefaultAction(action)
                action.triggered.connect(self.OnColorChanged)
                pBtnColor.setToolTip(str(theme_colors[i][j].getRgb()))
                pGridLayout.addWidget(pBtnColor, i, j)

标准色块的设置基本类同,注意setAutoRaise是让QAction鼠标在上面时凸起,动作自然些。

        # 设置标准色标签
        btitle = QLabel(u"标准色")
        btitle.setStyleSheet("QLabel{background:lightgray;color:black;font:bold 8pt '微软雅黑'}")

        # 设置颜色1*10大小的标准色框架,利用QGridLayout布局放置颜色块
        bGridLayout = QGridLayout()
        bGridLayout.setAlignment(Qt.AlignCenter)
        bGridLayout.setContentsMargins(0, 0, 0, 0)
        bGridLayout.setSpacing(2)
        for m in range(10):
            baction = QAction()
            baction.setData(basic_colors[m])
            baction.setIcon(self.createColorIcon(basic_colors[m]))
            bBtnColor = QToolButton()
            bBtnColor.setFixedSize(QSize(20, 20))
            bBtnColor.setAutoRaise(True)
            bBtnColor.setDefaultAction(baction)
            baction.triggered.connect(self.OnColorChanged)
            bBtnColor.setToolTip(str(basic_colors[m].getRgb()))
            bGridLayout.addWidget(bBtnColor, 0, m)

接下来,是设置分割线、添加二级QToolbutton的操作。这里QIcon用了QPainter进行自定义绘制,主要设计了画笔粗细和虚线设置的两个Icon。注意!其中为了QToolButton的三角下拉符合不显示,进行了setStyleSheet设置,这里十分关键!!!

        # 设置标准色标签
        btitle = QLabel(u"标准色")
        btitle.setStyleSheet("QLabel{background:lightgray;color:black;font:bold 8pt '微软雅黑'}")

        # 设置颜色1*10大小的标准色框架,利用QGridLayout布局放置颜色块
        bGridLayout = QGridLayout()
        bGridLayout.setAlignment(Qt.AlignCenter)
        bGridLayout.setContentsMargins(0, 0, 0, 0)
        bGridLayout.setSpacing(2)
        for m in range(10):
            baction = QAction()
            baction.setData(basic_colors[m])
            baction.setIcon(self.createColorIcon(basic_colors[m]))
            bBtnColor = QToolButton()
            bBtnColor.setFixedSize(QSize(20, 20))
            bBtnColor.setAutoRaise(True)
            bBtnColor.setDefaultAction(baction)
            baction.triggered.connect(self.OnColorChanged)
            bBtnColor.setToolTip(str(basic_colors[m].getRgb()))
            bGridLayout.addWidget(bBtnColor, 0, m)

        # 设置分割水平线,利用QFrame
        line = QFrame()
        line.setFrameShape(QFrame.HLine)
        line.setFrameShadow(QFrame.Plain)

        # 设置“无边框(透明色)”按钮功能
        pBtnTransparent = QToolButton()
        pBtnTransparent.setArrowType(Qt.NoArrow)
        pBtnTransparent.setToolButtonStyle(Qt.ToolButtonTextBesideIcon)
        pBtnTransparent.setFixedSize(218, 20)
        pBtnTransparent.setAutoRaise(True)
        pBtnTransparent.setStyleSheet("QToolButton{font:bold 8pt '微软雅黑'}")
        pBtnTransparent.setText("无边框颜色")
        pBtnTransparent.setIcon(QIcon("Frame.png"))
        pBtnTransparent.setIconSize(QSize(20, 20))
        pBtnTransparent.clicked.connect(self.set_pen_Transparent)

        # 设置“选择其他颜色”按钮功能
        othercolor_btn = QToolButton()
        othercolor_btn.setArrowType(Qt.NoArrow)
        othercolor_btn.setToolButtonStyle(Qt.ToolButtonTextBesideIcon)
        othercolor_btn.setFixedSize(218, 20)
        othercolor_btn.setAutoRaise(True)
        othercolor_btn.setIcon(QIcon("color.png"))
        othercolor_btn.setText(u"选择其他颜色")
        othercolor_btn.setIconSize(QSize(15, 15))
        othercolor_btn.setStyleSheet("QToolButton{font:bold 8pt '微软雅黑'}")
        othercolor_btn.clicked.connect(self.on_colorboard_show)

        # 将设置好的颜色框架,用QWidget包装好
        widget = QWidget()
        widget.setLayout(pGridLayout)
        bwidget = QWidget()
        bwidget.setLayout(bGridLayout)

        #  将上述设置的这些所有颜色框架,小组件窗口,用QVBoxLayout包装好
        pVLayout = QVBoxLayout()
        pVLayout.setSpacing(1)
        pVLayout.addWidget(title)
        pVLayout.addWidget(widget)
        pVLayout.addWidget(btitle)
        pVLayout.addWidget(bwidget)
        pVLayout.addWidget(line)
        pVLayout.addWidget(pBtnTransparent)
        pVLayout.addWidget(othercolor_btn)

        # 设置分割水平线,利用QFrame
        line2 = QFrame()
        line2.setFrameShape(QFrame.HLine)
        line2.setFrameShadow(QFrame.Plain)
        pVLayout.addWidget(line2)

        # 画笔粗细按钮
        self.thicknessbtn = QToolButton(self, text="粗细")
        self.thicknessbtn.setFixedSize(218, 20)
        self.thicknessbtn.setAutoRaise(True)
        self.thicknessbtn.setToolButtonStyle(Qt.ToolButtonTextBesideIcon)

        # 自定义画笔粗细的QIcon
        thickIcon = QPixmap(30, 30)
        thickIcon.fill(Qt.white)
        thickpainter = QPainter(thickIcon)
        d = 5
        for k in range(4):
            thickpainter.setPen(QPen(Qt.black, k + 1, Qt.SolidLine))
            thickpainter.drawLine(0, (d + 1) * k + 5, 30, (d + 1) * k + 5)
        thickpainter.end()
        self.thicknessbtn.setIcon(QIcon(thickIcon))

        self.thicknessbtn.setPopupMode(QToolButton.InstantPopup)
        self.thicknessbtn.setArrowType(Qt.NoArrow)
        self.thicknessbtn.setStyleSheet(
            "QToolButton::menu-indicator {image: none;} QToolButton{font:bold 8pt '微软雅黑'}")

        tLayout = QVBoxLayout()
        tLayout.setSpacing(0)
        self.thicknessmenu = QMenu(self)
        for i in range(10):
            action = QAction(parent=self.thicknessmenu)
            action.setData(i)
            action.setIcon(self.set_width_Icon(i + 1))
            action.setText("{}磅".format(i + 1))
            pBtnWidth = QToolButton()
            pBtnWidth.setToolButtonStyle(Qt.ToolButtonTextBesideIcon)
            pBtnWidth.setIconSize(QSize(100, 10))
            pBtnWidth.setStyleSheet(
                "QToolButton::menu-indicator {image: none;}")
            pBtnWidth.setAutoRaise(True)
            pBtnWidth.setDefaultAction(action)
            action.triggered.connect(self.OnWidthChanged)
            pBtnWidth.setToolTip(str("粗细:{}磅".format(i + 1)))
            tLayout.addWidget(pBtnWidth, i)
        self.twidget = QWidget()
        self.twidget.setLayout(tLayout)
        tVLayout = QVBoxLayout()
        tVLayout.setSpacing(1)
        tVLayout.setContentsMargins(1, 1, 1, 1)
        tVLayout.addWidget(self.twidget)
        self.thicknessmenu.setLayout(tVLayout)
        self.thicknessbtn.setMenu(self.thicknessmenu)
        self.thicknessmenu.showEvent = self.thickness_show
        pVLayout.addWidget(self.thicknessbtn)

        # 画笔虚线设定
        style = [Qt.NoPen, Qt.SolidLine, Qt.DashLine, Qt.DotLine,
                 Qt.DashDotLine, Qt.DashDotDotLine, Qt.CustomDashLine]
        name = ["无", "实线", "虚线", "点线", "点虚线", "点点虚线", "自定义"]

        # 画笔虚线按钮
        self.stylebtn = QToolButton(self, text="虚线")
        self.stylebtn.setFixedSize(218, 20)
        self.stylebtn.setAutoRaise(True)
        self.stylebtn.setToolButtonStyle(Qt.ToolButtonTextBesideIcon)

        # 自定义画笔虚线的QIcon
        styleIcon = QPixmap(30, 30)
        styleIcon.fill(Qt.white)
        stylepainter = QPainter(styleIcon)
        f = 5
        for k in range(4):
            stylepainter.setPen(style[k + 1])
            stylepainter.drawLine(0, (f + 1) * k + 5, 30, (f + 1) * k + 5)
        stylepainter.end()
        self.stylebtn.setIcon(QIcon(styleIcon))
        self.stylebtn.setPopupMode(QToolButton.InstantPopup)
        self.stylebtn.setArrowType(Qt.NoArrow)
        self.stylebtn.setStyleSheet(
            "QToolButton::menu-indicator {image: none;} QToolButton{font:bold 8pt '微软雅黑'}")

        sLayout = QVBoxLayout()
        sLayout.setSpacing(0)
        self.stylemenu = QMenu(self)
        for j in range(7):
            saction = QAction(parent=self.stylemenu)
            saction.setData(style[j])
            saction.setIcon(self.set_style_Icon(style[j]))
            saction.setText(name[j])
            sBtnStyle = QToolButton()
            sBtnStyle.setToolButtonStyle(Qt.ToolButtonTextBesideIcon)
            sBtnStyle.setIconSize(QSize(100, 10))
            sBtnStyle.setStyleSheet(
                "QToolButton::menu-indicator {image: none;}")
            sBtnStyle.setAutoRaise(True)
            sBtnStyle.setDefaultAction(saction)
            saction.triggered.connect(self.OnStyleChanged)
            sBtnStyle.setToolTip(str(style[j]))
            sLayout.addWidget(sBtnStyle, j)

        self.swidget = QWidget()
        self.swidget.setLayout(sLayout)
        sVLayout = QVBoxLayout()
        sVLayout.setSpacing(1)
        sVLayout.setContentsMargins(1, 1, 1, 1)
        sVLayout.addWidget(self.swidget)
        self.stylemenu.setLayout(sVLayout)
        self.stylebtn.setMenu(self.stylemenu)
        self.stylemenu.showEvent = self.style_show
        pVLayout.addWidget(self.stylebtn)

然接下来就是设置总的下拉框按钮,并使图标是动态绘制的。这里,QICon分为两步,一个是外部图标icon,另外一个是Qpainter绘制在Qpixmap中的的填充颜色矩形,然后两者合并成一个Icon即可。

        # 设置下拉框总按钮
        self.ColorCombox = QToolButton()
        self.ColorCombox.setAutoRaise(True)
        self.ColorCombox.setPopupMode(QToolButton.InstantPopup)  # 设置下拉框按钮按下时弹出菜单窗口
        self.ColorCombox.setToolButtonStyle(Qt.ToolButtonTextBesideIcon)
        self.ColorCombox.setArrowType(Qt.NoArrow)
        self.ColorCombox.setText("画笔颜色")
        # self.ColorCombox.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Fixed)
        # self.ColorCombox.setMinimumSize(100, 30)
        self.ColorCombox.setAutoFillBackground(True)
        # 利用setStyleSheet设置QToolButton不显示下箭头
        self.ColorCombox.setStyleSheet("QToolButton::menu-indicator {image: none;} QToolButton{font:bold 9pt '微软雅黑'}")

        # 设置颜色下拉按钮的自定义图标Icon,这里是初始化
        qp = QPixmap(30, 30)  # 设置QPixmap场景大小
        qp.fill(Qt.transparent)
        self.pix = QPixmap()
        self.pix.load("noun_Pen.png")  # 这是画笔Icon,请替换成自己的图片或者利用QPainter画出笔也行
        pixfixed = self.pix.scaled(25, 25, Qt.KeepAspectRatio, Qt.SmoothTransformation)
        target = QRect(0, 0, 20, 20)
        source = QRect(0, 0, 20, 20)
        painter = QPainter(qp)  # 设置QPainter在自己设的QPixmap上画
        painter.setRenderHint(QPainter.Antialiasing)
        painter.setRenderHint(QPainter.SmoothPixmapTransform)
        painter.drawPixmap(target, pixfixed, source)
        painter.fillRect(QRect(0, 22, 22, 5), Qt.red)
        painter.end()
        self.ColorCombox.setIcon(QIcon(qp))
        self.ColorCombox.setIconSize(QSize(30, 30))

然后就是将上述这些设计好的自定义QWidget窗口,打包进一个QMenu当中,并设置。

        # 设置弹出菜单,菜单打上上述打包好所有颜色框架、窗口的pVLayout内容
        self.colorMenu = QMenu(self)
        self.colorMenu.setLayout(pVLayout)

        # 设置下拉框按钮菜单为上述菜单
        self.ColorCombox.setMenu(self.colorMenu)

        ### 将所有上述打包好的内,用本类设置的QWidget打包成窗口控件 ###
        alLayout = QVBoxLayout()
        alLayout.setSpacing(0)
        alLayout.addWidget(self.ColorCombox)
        self.setLayout(alLayout)

最后,当然是设置上述代码中QAction、QToolbutton对应的槽函数。有一项需特殊说明:那就是菜单中的按钮弹出二级菜单的方法和位置,这里为了让二级菜单不默认弹出在QToolButton的下侧,这里改写了showEvent的方法,这里设置了两个showEvent函数”thickness_show“和”style_show“。

    ### ——以下为本类所用到的函数—— ###

    # 重设画笔粗细按钮按下后菜单出现在右侧
    def thickness_show(self, e):
        parent = self.colorMenu.pos()
        pos = self.thicknessbtn.geometry()
        m = self.thicknessmenu.geometry()
        w = pos.width()
        self.thicknessmenu.move(parent.x() + w + 16, m.y() - pos.height())

    # 重设画笔虚线按钮按下后菜单出现在右侧
    def style_show(self, e):
        parent = self.colorMenu.pos()
        pos = self.stylebtn.geometry()
        m = self.stylemenu.geometry()
        w = pos.width()
        self.stylemenu.move(parent.x() + w + 16, m.y() - pos.height())

    # 设置画笔粗细菜单栏中的所有Icon图标
    def set_width_Icon(self, width):
        color = Qt.black
        pix = QPixmap(100, width)
        pix.fill(QColor(color))
        return QIcon(pix)

    # 设置画笔粗细选中时的操作
    def OnWidthChanged(self):
        width = self.sender().data() + 1
        # print(width)
        self.thicknessmenu.close()
        self.colorMenu.close()
        self.thick_signal.emit(width)

    # 设置画笔虚线菜单栏中的所有Icon图标
    def set_style_Icon(self, style):
        # print(style)
        color = Qt.black
        pix = QPixmap(100, 6)
        pix.fill(Qt.white)
        painter = QPainter(pix)
        pp = QPen()
        pp.setStyle(style)
        pp.setColor(color)
        pp.setWidth(3)
        painter.setPen(pp)
        painter.drawLine(0, 3, 100, 3)
        painter.end()
        return QIcon(pix)

    # 设置画笔虚线形状选中时的操作
    def OnStyleChanged(self):
        style = self.sender().data()
        # print(Qt.PenStyle(style))
        self.stylemenu.close()
        self.colorMenu.close()
        self.style_signal.emit(style)

    # 用于设置QAction颜色块的槽函数
    def createColorIcon(self, color):
        pixmap = QPixmap(18, 18)
        pixmap.fill(QColor(color))
        return QIcon(pixmap)

    # 当透明色设置按钮按下后的槽函数
    def set_pen_Transparent(self):
        color = Qt.transparent
        self.colorMenu.close()
        self.signal.emit(color)

    # 设置颜色下拉按钮的自定义图标Icon,这里是颜色变化时改变图标下层矩形填充颜色
    def createColorToolButtonIcon(self, color):
        # print(color)
        qp = QPixmap(30, 30)
        qp.fill(Qt.transparent)
        self.pix = QPixmap()
        self.pix.load("noun_Pen.png")
        pixfix = self.pix.scaled(25, 25, Qt.KeepAspectRatio, Qt.SmoothTransformation)
        target = QRect(0, 0, 20, 20)
        source = QRect(0, 0, 20, 20)
        painter = QPainter(qp)
        painter.setRenderHint(QPainter.Antialiasing)
        painter.setRenderHint(QPainter.SmoothPixmapTransform)
        painter.drawPixmap(target, pixfix, source)
        painter.fillRect(QRect(0, 22, 22, 5), color)
        painter.end()
        self.ColorCombox.setIcon(QIcon(qp))
        self.ColorCombox.setIconSize(QSize(30, 30))

    # 当颜色色块QAction按下后的槽函数
    def OnColorChanged(self):
        color = self.sender().data()
        self.colorMenu.close()
        self.signal.emit(color)

    # 当其他颜色按钮按下时弹出Qt自带的颜色选择器
    def on_colorboard_show(self):
        color = QColorDialog.getColor(Qt.black, self)
        if color.isValid():
            self.signal.emit(color)
            return color

4、完整代码

import sys
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
from PyQt5.Qt import QPixmap, QPainter, QPoint, QPaintEvent, QMouseEvent, QPen, \
    QColor, QSize


class ColorCombox(QWidget):
    # 发送颜色更改信号,类型为Qcolor的object类型
    signal = pyqtSignal(object)
    thick_signal = pyqtSignal(object)
    style_signal = pyqtSignal(object)

    def __init__(self):
        super().__init__()
        # 设置场景颜色,大小是6*10二维列表
        theme_colors = [
            [QColor(255, 255, 255, 255), QColor(0, 0, 0, 255), QColor(231, 230, 230, 255), QColor(64, 84, 106, 255),
             QColor(91, 155, 213, 255), QColor(237, 124, 48, 255), QColor(165, 165, 165, 255), QColor(255, 192, 0, 255),
             QColor(68, 114, 196, 255), QColor(112, 173, 71, 255)],

            [QColor(242, 242, 242, 255), QColor(127, 127, 127, 255), QColor(208, 206, 206, 255),
             QColor(214, 220, 228, 255),
             QColor(222, 235, 246, 255), QColor(251, 229, 213, 255), QColor(237, 237, 237, 237),
             QColor(255, 242, 204, 255), QColor(217, 226, 243, 255), QColor(226, 239, 217, 255)],

            [QColor(216, 216, 216, 255), QColor(89, 89, 89, 255), QColor(174, 171, 171, 255),
             QColor(173, 185, 202, 255),
             QColor(189, 215, 238, 255), QColor(247, 203, 172, 255), QColor(219, 219, 219, 255),
             QColor(254, 229, 153, 255), QColor(180, 198, 231, 255), QColor(197, 224, 179, 255)],

            [QColor(191, 191, 191, 255), QColor(63, 63, 63, 255), QColor(117, 112, 112, 255),
             QColor(132, 150, 176, 255),
             QColor(156, 195, 229, 255), QColor(244, 177, 131, 255), QColor(201, 201, 201, 255),
             QColor(255, 217, 101, 255), QColor(142, 170, 219, 255), QColor(168, 208, 141, 255)],

            [QColor(165, 165, 165, 255), QColor(38, 38, 38, 255), QColor(58, 56, 56, 255), QColor(50, 63, 79, 255),
             QColor(39, 112, 179, 255), QColor(197, 90, 17, 255), QColor(123, 123, 123, 255), QColor(191, 144, 0, 255),
             QColor(47, 84, 150, 255), QColor(83, 129, 53, 255)],

            [QColor(124, 124, 124, 255), QColor(12, 12, 12, 255), QColor(23, 22, 22, 255), QColor(34, 42, 53, 255),
             QColor(34, 81, 123, 255), QColor(124, 48, 2, 255), QColor(82, 82, 82, 255), QColor(127, 96, 0, 255),
             QColor(31, 56, 100, 255), QColor(55, 86, 35, 255)]
        ]

        # 设置基础颜色,大小是1*10一维列表
        basic_colors = [
            QColor(192, 0, 0, 255), QColor(255, 0, 0, 255), QColor(255, 192, 0, 255),
            QColor(255, 255, 0, 255), QColor(146, 208, 80, 255), QColor(0, 176, 80, 255),
            QColor(0, 176, 240, 255), QColor(0, 112, 192, 255), QColor(0, 32, 96, 255),
            QColor(112, 48, 160, 255)
        ]

        # 设置下拉框总按钮
        self.ColorCombox = QToolButton()
        self.ColorCombox.setAutoRaise(True)
        self.ColorCombox.setPopupMode(QToolButton.InstantPopup)  # 设置下拉框按钮按下时弹出菜单窗口
        self.ColorCombox.setToolButtonStyle(Qt.ToolButtonTextBesideIcon)
        self.ColorCombox.setArrowType(Qt.NoArrow)
        self.ColorCombox.setText("画笔颜色")
        # self.ColorCombox.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Fixed)
        # self.ColorCombox.setMinimumSize(100, 30)
        self.ColorCombox.setAutoFillBackground(True)
        # 利用setStyleSheet设置QToolButton不显示下箭头
        self.ColorCombox.setStyleSheet("QToolButton::menu-indicator {image: none;} QToolButton{font:bold 9pt '微软雅黑'}")

        # 设置颜色下拉按钮的自定义图标Icon,这里是初始化
        qp = QPixmap(30, 30)  # 设置QPixmap场景大小
        qp.fill(Qt.transparent)
        self.pix = QPixmap()
        self.pix.load("noun_Pen.png")  # 这是画笔Icon,请替换成自己的图片或者利用QPainter画出笔也行
        pixfixed = self.pix.scaled(25, 25, Qt.KeepAspectRatio, Qt.SmoothTransformation)
        target = QRect(0, 0, 20, 20)
        source = QRect(0, 0, 20, 20)
        painter = QPainter(qp)  # 设置QPainter在自己设的QPixmap上画
        painter.setRenderHint(QPainter.Antialiasing)
        painter.setRenderHint(QPainter.SmoothPixmapTransform)
        painter.drawPixmap(target, pixfixed, source)
        painter.fillRect(QRect(0, 22, 22, 5), Qt.red)
        painter.end()
        self.ColorCombox.setIcon(QIcon(qp))
        self.ColorCombox.setIconSize(QSize(30, 30))

        # 设置主题色标签
        title = QLabel(u"主题颜色")
        title.setStyleSheet("QLabel{background:lightgray;color:black;font:bold 8pt '微软雅黑'}")

        # 设置颜色6*10大小的主题颜色框架,利用QGridLayout布局放置颜色块
        pGridLayout = QGridLayout()
        pGridLayout.setAlignment(Qt.AlignCenter)
        pGridLayout.setContentsMargins(0, 0, 0, 0)
        pGridLayout.setSpacing(2)
        for i in range(6):
            for j in range(10):
                action = QAction()
                action.setData(theme_colors[i][j])
                action.setIcon(self.createColorIcon(theme_colors[i][j]))
                pBtnColor = QToolButton()
                pBtnColor.setFixedSize(QSize(20, 20))
                pBtnColor.setAutoRaise(True)
                pBtnColor.setDefaultAction(action)
                action.triggered.connect(self.OnColorChanged)
                pBtnColor.setToolTip(str(theme_colors[i][j].getRgb()))
                pGridLayout.addWidget(pBtnColor, i, j)

        # 设置标准色标签
        btitle = QLabel(u"标准色")
        btitle.setStyleSheet("QLabel{background:lightgray;color:black;font:bold 8pt '微软雅黑'}")

        # 设置颜色1*10大小的标准色框架,利用QGridLayout布局放置颜色块
        bGridLayout = QGridLayout()
        bGridLayout.setAlignment(Qt.AlignCenter)
        bGridLayout.setContentsMargins(0, 0, 0, 0)
        bGridLayout.setSpacing(2)
        for m in range(10):
            baction = QAction()
            baction.setData(basic_colors[m])
            baction.setIcon(self.createColorIcon(basic_colors[m]))
            bBtnColor = QToolButton()
            bBtnColor.setFixedSize(QSize(20, 20))
            bBtnColor.setAutoRaise(True)
            bBtnColor.setDefaultAction(baction)
            baction.triggered.connect(self.OnColorChanged)
            bBtnColor.setToolTip(str(basic_colors[m].getRgb()))
            bGridLayout.addWidget(bBtnColor, 0, m)

        # 设置分割水平线,利用QFrame
        line = QFrame()
        line.setFrameShape(QFrame.HLine)
        line.setFrameShadow(QFrame.Plain)

        # 设置“无边框(透明色)”按钮功能
        pBtnTransparent = QToolButton()
        pBtnTransparent.setArrowType(Qt.NoArrow)
        pBtnTransparent.setToolButtonStyle(Qt.ToolButtonTextBesideIcon)
        pBtnTransparent.setFixedSize(218, 20)
        pBtnTransparent.setAutoRaise(True)
        pBtnTransparent.setStyleSheet("QToolButton{font:bold 8pt '微软雅黑'}")
        pBtnTransparent.setText("无边框颜色")
        pBtnTransparent.setIcon(QIcon("Frame.png"))
        pBtnTransparent.setIconSize(QSize(20, 20))
        pBtnTransparent.clicked.connect(self.set_pen_Transparent)

        # 设置“选择其他颜色”按钮功能
        othercolor_btn = QToolButton()
        othercolor_btn.setArrowType(Qt.NoArrow)
        othercolor_btn.setToolButtonStyle(Qt.ToolButtonTextBesideIcon)
        othercolor_btn.setFixedSize(218, 20)
        othercolor_btn.setAutoRaise(True)
        othercolor_btn.setIcon(QIcon("color.png"))
        othercolor_btn.setText(u"选择其他颜色")
        othercolor_btn.setIconSize(QSize(15, 15))
        othercolor_btn.setStyleSheet("QToolButton{font:bold 8pt '微软雅黑'}")
        othercolor_btn.clicked.connect(self.on_colorboard_show)

        # 将设置好的颜色框架,用QWidget包装好
        widget = QWidget()
        widget.setLayout(pGridLayout)
        bwidget = QWidget()
        bwidget.setLayout(bGridLayout)

        #  将上述设置的这些所有颜色框架,小组件窗口,用QVBoxLayout包装好
        pVLayout = QVBoxLayout()
        pVLayout.setSpacing(1)
        pVLayout.addWidget(title)
        pVLayout.addWidget(widget)
        pVLayout.addWidget(btitle)
        pVLayout.addWidget(bwidget)
        pVLayout.addWidget(line)
        pVLayout.addWidget(pBtnTransparent)
        pVLayout.addWidget(othercolor_btn)

        # 设置分割水平线,利用QFrame
        line2 = QFrame()
        line2.setFrameShape(QFrame.HLine)
        line2.setFrameShadow(QFrame.Plain)
        pVLayout.addWidget(line2)

        # 画笔粗细按钮
        self.thicknessbtn = QToolButton(self, text="粗细")
        self.thicknessbtn.setFixedSize(218, 20)
        self.thicknessbtn.setAutoRaise(True)
        self.thicknessbtn.setToolButtonStyle(Qt.ToolButtonTextBesideIcon)

        # 自定义画笔粗细的QIcon
        thickIcon = QPixmap(30, 30)
        thickIcon.fill(Qt.white)
        thickpainter = QPainter(thickIcon)
        d = 5
        for k in range(4):
            thickpainter.setPen(QPen(Qt.black, k + 1, Qt.SolidLine))
            thickpainter.drawLine(0, (d + 1) * k + 5, 30, (d + 1) * k + 5)
        thickpainter.end()
        self.thicknessbtn.setIcon(QIcon(thickIcon))

        self.thicknessbtn.setPopupMode(QToolButton.InstantPopup)
        self.thicknessbtn.setArrowType(Qt.NoArrow)
        self.thicknessbtn.setStyleSheet(
            "QToolButton::menu-indicator {image: none;} QToolButton{font:bold 8pt '微软雅黑'}")

        tLayout = QVBoxLayout()
        tLayout.setSpacing(0)
        self.thicknessmenu = QMenu(self)
        for i in range(10):
            action = QAction(parent=self.thicknessmenu)
            action.setData(i)
            action.setIcon(self.set_width_Icon(i + 1))
            action.setText("{}磅".format(i + 1))
            pBtnWidth = QToolButton()
            pBtnWidth.setToolButtonStyle(Qt.ToolButtonTextBesideIcon)
            pBtnWidth.setIconSize(QSize(100, 10))
            pBtnWidth.setStyleSheet(
                "QToolButton::menu-indicator {image: none;}")
            pBtnWidth.setAutoRaise(True)
            pBtnWidth.setDefaultAction(action)
            action.triggered.connect(self.OnWidthChanged)
            pBtnWidth.setToolTip(str("粗细:{}磅".format(i + 1)))
            tLayout.addWidget(pBtnWidth, i)
        self.twidget = QWidget()
        self.twidget.setLayout(tLayout)
        tVLayout = QVBoxLayout()
        tVLayout.setSpacing(1)
        tVLayout.setContentsMargins(1, 1, 1, 1)
        tVLayout.addWidget(self.twidget)
        self.thicknessmenu.setLayout(tVLayout)
        self.thicknessbtn.setMenu(self.thicknessmenu)
        self.thicknessmenu.showEvent = self.thickness_show
        pVLayout.addWidget(self.thicknessbtn)

        # 画笔虚线设定
        style = [Qt.NoPen, Qt.SolidLine, Qt.DashLine, Qt.DotLine,
                 Qt.DashDotLine, Qt.DashDotDotLine, Qt.CustomDashLine]
        name = ["无", "实线", "虚线", "点线", "点虚线", "点点虚线", "自定义"]

        # 画笔虚线按钮
        self.stylebtn = QToolButton(self, text="虚线")
        self.stylebtn.setFixedSize(218, 20)
        self.stylebtn.setAutoRaise(True)
        self.stylebtn.setToolButtonStyle(Qt.ToolButtonTextBesideIcon)

        # 自定义画笔虚线的QIcon
        styleIcon = QPixmap(30, 30)
        styleIcon.fill(Qt.white)
        stylepainter = QPainter(styleIcon)
        f = 5
        for k in range(4):
            stylepainter.setPen(style[k + 1])
            stylepainter.drawLine(0, (f + 1) * k + 5, 30, (f + 1) * k + 5)
        stylepainter.end()
        self.stylebtn.setIcon(QIcon(styleIcon))
        self.stylebtn.setPopupMode(QToolButton.InstantPopup)
        self.stylebtn.setArrowType(Qt.NoArrow)
        self.stylebtn.setStyleSheet(
            "QToolButton::menu-indicator {image: none;} QToolButton{font:bold 8pt '微软雅黑'}")

        sLayout = QVBoxLayout()
        sLayout.setSpacing(0)
        self.stylemenu = QMenu(self)
        for j in range(7):
            saction = QAction(parent=self.stylemenu)
            saction.setData(style[j])
            saction.setIcon(self.set_style_Icon(style[j]))
            saction.setText(name[j])
            sBtnStyle = QToolButton()
            sBtnStyle.setToolButtonStyle(Qt.ToolButtonTextBesideIcon)
            sBtnStyle.setIconSize(QSize(100, 10))
            sBtnStyle.setStyleSheet(
                "QToolButton::menu-indicator {image: none;}")
            sBtnStyle.setAutoRaise(True)
            sBtnStyle.setDefaultAction(saction)
            saction.triggered.connect(self.OnStyleChanged)
            sBtnStyle.setToolTip(str(style[j]))
            sLayout.addWidget(sBtnStyle, j)

        self.swidget = QWidget()
        self.swidget.setLayout(sLayout)
        sVLayout = QVBoxLayout()
        sVLayout.setSpacing(1)
        sVLayout.setContentsMargins(1, 1, 1, 1)
        sVLayout.addWidget(self.swidget)
        self.stylemenu.setLayout(sVLayout)
        self.stylebtn.setMenu(self.stylemenu)
        self.stylemenu.showEvent = self.style_show
        pVLayout.addWidget(self.stylebtn)

        # 设置弹出菜单,菜单打上上述打包好所有颜色框架、窗口的pVLayout内容
        self.colorMenu = QMenu(self)
        self.colorMenu.setLayout(pVLayout)

        # 设置下拉框按钮菜单为上述菜单
        self.ColorCombox.setMenu(self.colorMenu)

        ### 将所有上述打包好的内,用本类设置的QWidget打包成窗口控件 ###
        alLayout = QVBoxLayout()
        alLayout.setSpacing(0)
        alLayout.addWidget(self.ColorCombox)
        self.setLayout(alLayout)

    ### ——以下为本类所用到的函数—— ###

    # 重设画笔粗细按钮按下后菜单出现在右侧
    def thickness_show(self, e):
        parent = self.colorMenu.pos()
        pos = self.thicknessbtn.geometry()
        m = self.thicknessmenu.geometry()
        w = pos.width()
        self.thicknessmenu.move(parent.x() + w + 16, m.y() - pos.height())

    # 重设画笔虚线按钮按下后菜单出现在右侧
    def style_show(self, e):
        parent = self.colorMenu.pos()
        pos = self.stylebtn.geometry()
        m = self.stylemenu.geometry()
        w = pos.width()
        self.stylemenu.move(parent.x() + w + 16, m.y() - pos.height())

    # 设置画笔粗细菜单栏中的所有Icon图标
    def set_width_Icon(self, width):
        color = Qt.black
        pix = QPixmap(100, width)
        pix.fill(QColor(color))
        return QIcon(pix)

    # 设置画笔粗细选中时的操作
    def OnWidthChanged(self):
        width = self.sender().data() + 1
        # print(width)
        self.thicknessmenu.close()
        self.colorMenu.close()
        self.thick_signal.emit(width)

    # 设置画笔虚线菜单栏中的所有Icon图标
    def set_style_Icon(self, style):
        # print(style)
        color = Qt.black
        pix = QPixmap(100, 6)
        pix.fill(Qt.white)
        painter = QPainter(pix)
        pp = QPen()
        pp.setStyle(style)
        pp.setColor(color)
        pp.setWidth(3)
        painter.setPen(pp)
        painter.drawLine(0, 3, 100, 3)
        painter.end()
        return QIcon(pix)

    # 设置画笔虚线形状选中时的操作
    def OnStyleChanged(self):
        style = self.sender().data()
        # print(Qt.PenStyle(style))
        self.stylemenu.close()
        self.colorMenu.close()
        self.style_signal.emit(style)

    # 用于设置QAction颜色块的槽函数
    def createColorIcon(self, color):
        pixmap = QPixmap(18, 18)
        pixmap.fill(QColor(color))
        return QIcon(pixmap)

    # 当透明色设置按钮按下后的槽函数
    def set_pen_Transparent(self):
        color = Qt.transparent
        self.colorMenu.close()
        self.signal.emit(color)

    # 设置颜色下拉按钮的自定义图标Icon,这里是颜色变化时改变图标下层矩形填充颜色
    def createColorToolButtonIcon(self, color):
        # print(color)
        qp = QPixmap(30, 30)
        qp.fill(Qt.transparent)
        self.pix = QPixmap()
        self.pix.load("noun_Pen.png")
        pixfix = self.pix.scaled(25, 25, Qt.KeepAspectRatio, Qt.SmoothTransformation)
        target = QRect(0, 0, 20, 20)
        source = QRect(0, 0, 20, 20)
        painter = QPainter(qp)
        painter.setRenderHint(QPainter.Antialiasing)
        painter.setRenderHint(QPainter.SmoothPixmapTransform)
        painter.drawPixmap(target, pixfix, source)
        painter.fillRect(QRect(0, 22, 22, 5), color)
        painter.end()
        self.ColorCombox.setIcon(QIcon(qp))
        self.ColorCombox.setIconSize(QSize(30, 30))

    # 当颜色色块QAction按下后的槽函数
    def OnColorChanged(self):
        color = self.sender().data()
        self.colorMenu.close()
        self.signal.emit(color)

    # 当其他颜色按钮按下时弹出Qt自带的颜色选择器
    def on_colorboard_show(self):
        color = QColorDialog.getColor(Qt.black, self)
        if color.isValid():
            self.signal.emit(color)
            return color





def main():
    app = QApplication([])
    QApplication.setStyle(QStyleFactory.create("Fusion"))
    window = ColorCombox()
    window.show()
    app.exec()


if __name__ == '__main__':
    main()

5、结束语

希望本文章能对你有所帮助,也希望个大佬指点不足,谢谢!

以下是参考的C++文章链接:

Qt颜色下拉框

你可能感兴趣的:(PyQt5,Qt,python,c++,pyqt5)