PyQt5 Linux Mint肉桂本地“删除”、“应用”按钮

2024-06-08 21:21:25 发布

您现在位置:Python中文网/ 问答频道 /正文

我正在尝试在LinuxMint19(肉桂色)上创建一个pyqt5应用程序。 我有一个“删除”和“应用”按钮,但我想让它们看起来更相关。如图所示:

本机Linux薄荷按钮

更多

这是LinuxMint上带有apply或delete角色的按钮的本机外观,我想在我的应用程序中创建这样的按钮,但我还没有找到这样做的方法

windows和mac似乎有qtmacextrasqtwinextras模块来处理这些东西。Linux有某种qtx11extras,但该模块不提供这种功能


Tags: 模块方法应用程序角色linuxwindowsdelete按钮
2条回答

您可以使用样式表根据自己的喜好进行设计

下面是一个独立的示例。当然,您必须使其适应您喜欢的颜色和尺寸。但这应该已经提供了一个起点

import sys

from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import QApplication, QWidget, QPushButton, QHBoxLayout

GREEN_BUTTON_STYLE = """
    QPushButton {
        border: 4px solid black;
        border-radius: 4px;
        color: #ffffff;
        background-color: #6db442;
        font-size: 28px;
        min-height: 40px;
        min-width: 128px;
        }
    QPushButton:pressed {
        background-color: #646464;
        }
    QPushButton:flat {
        border: none;
        }
"""
RED_BUTTON_STYLE = """
    QPushButton {
        border: 4px solid black;
        border-radius: 4px;
        color: #ffffff;
        background-color: #f04a50;
        font-size: 26px;
        min-height: 40px;
        min-width: 128px;
        }
    QPushButton:pressed {
        background-color: #646464;
        }
    QPushButton:flat {
        border: none;
        }
"""


class App(QWidget):

    def __init__(self):
        super().__init__()
        self.title = 'button styles'
        self.init_ui()

    def init_ui(self):
        self.setWindowTitle(self.title)
        self.setGeometry(256, 128, 384, 256)

        button1 = QPushButton('Launch')
        button1.setStyleSheet(GREEN_BUTTON_STYLE)
        button1.setFlat(True)
        button1.clicked.connect(self.on_click)

        button2 = QPushButton('Remove')
        button2.setStyleSheet(RED_BUTTON_STYLE)
        button2.setFlat(True)
        button2.clicked.connect(self.on_click)

        layout = QHBoxLayout()
        layout.addWidget(button1, alignment=Qt.AlignHCenter)
        layout.addWidget(button2, alignment=Qt.AlignHCenter)
        self.setLayout(layout)
        self.setStyleSheet("background-color:#333333;")
        self.show()

    def on_click(self):
        print('clicked')


if __name__ == '__main__':
    app = QApplication(sys.argv)
    ex = App()
    sys.exit(app.exec_())

输出

它看起来是这样的:

styled buttons

虽然已经提供的解决方案可以很好地工作,但并不完全适用于整个应用程序,特别是如果您想要控制对话框按钮的外观

即使将样式表应用于应用程序,也需要确保在父级中正确设置选择器:例如,在另一个答案中,设置没有选择器的背景属性将清除子级的应用程序样式表继承

假设将样式表设置为应用程序,并且仔细编写了所有其他样式表集,那么对话框窗口就会出现问题

如果对话框是手动创建的,则可以通过为每个按钮指定样式表来设置替换颜色,但对于使用静态函数创建的按钮,这是不可能的

在这种情况下,唯一的可能是使用QProxyStyle。下面是一个可能的实现,它还允许设置自定义颜色和字体,并自动为对话框按钮框(取消、忽略等)的“负面”角色设置备用颜色

在本例中,我刚刚将样式应用于应用程序,但消息框是使用information()静态函数创建的。“备用”按钮是使用自定义属性手动设置的:button.setProperty('alternateColor', True)

Cool colored buttons!

class ColorButtonStyle(QtWidgets.QProxyStyle):
    def __init__(self, *args, **kwargs):
        if isinstance(kwargs.get('buttonFont'), QtGui.QFont):
            self._buttonFont = kwargs.pop('buttonFont')
        else:
            self._buttonFont = QtWidgets.QApplication.font()
            self._buttonFont.setPointSize(20)
        super().__init__(*args, **kwargs)
        self._buttonFontMetrics = QtGui.QFontMetrics(self._buttonFont)
        self._defaultButtonColor = QtGui.QColor(109, 180, 66)
        self._defaultTextColor = QtGui.QColor(QtCore.Qt.white)
        self._alternateButtonColor = QtGui.QColor(240, 74, 80)
        self._alternateTextColor = None
        self._alternateRoles = set((
            QtWidgets.QDialogButtonBox.RejectRole, 
            QtWidgets.QDialogButtonBox.DestructiveRole, 
            QtWidgets.QDialogButtonBox.NoRole, 
        ))

    def _polishApp(self):
        self.polish(QtWidgets.QApplication.instance())

    def buttonFont(self):
        return QtGui.QFont(self._buttonFont)

    @QtCore.pyqtSlot(QtGui.QFont)
    def setButtonFont(self, font):
        if not isinstance(font, QtGui.QFont) or font == self._buttonFont:
            return
        self._buttonFont = font
        self._buttonFontMetrics = QtGui.QFontMetrics(self._buttonFont)
        self._polishApp()

    def defaultButtonColor(self):
        return QtGui.QColor(self._defaultButtonColor)

    @QtCore.pyqtSlot(QtCore.Qt.GlobalColor)
    @QtCore.pyqtSlot(QtGui.QColor)
    def setDefaultButtonColor(self, color):
        if isinstance(color, QtCore.Qt.GlobalColor):
            color = QtGui.QColor(color)
        elif not isinstance(color, QtGui.QColor):
            return
        self._defaultButtonColor = color
        self._polishApp()

    def alternateButtonColor(self):
        return QtGui.QColor(self._alternateButtonColor)

    @QtCore.pyqtSlot(QtCore.Qt.GlobalColor)
    @QtCore.pyqtSlot(QtGui.QColor)
    def setAlternateButtonColor(self, color):
        if isinstance(color, QtCore.Qt.GlobalColor):
            color = QtGui.QColor(color)
        elif not isinstance(color, QtGui.QColor):
            return
        self._alternateButtonColor = color
        self._polishApp()

    def alternateRoles(self):
        return self._alternateRoles

    def setAlternateRoles(self, roles):
        newRoles = set()
        for role in roles:
            if isinstance(role, QtWidgets.QDialogButtonBox.ButtonRole):
                newRoles.add(role)
        if newRoles != self._alternateRoles:
            self._alternateRoles = newRoles
            self._polishApp()

    def setAlternateRole(self, role, activate=True):
        if isinstance(role, QtWidgets.QDialogButtonBox.ButtonRole):
            if activate and role in self._alternateRoles:
                self._alternateRoles.add(role)
                self._polishApp()
            elif not activate and role not in self._alternateRoles:
                self._alternateRoles.remove(role)
                self._polishApp()

    def defaultTextColor(self):
        return QtGui.QColor(self._defaultTextColor)

    @QtCore.pyqtSlot(QtCore.Qt.GlobalColor)
    @QtCore.pyqtSlot(QtGui.QColor)
    def setDefaultTextColor(self, color):
        if isinstance(color, QtCore.Qt.GlobalColor):
            color = QtGui.QColor(color)
        elif not isinstance(color, QtGui.QColor):
            return
        self._defaultTextColor = color
        self._polishApp()

    def alternateTextColor(self):
        return QtGui.QColor(self._alternateTextColor or self._defaultTextColor)

    @QtCore.pyqtSlot(QtCore.Qt.GlobalColor)
    @QtCore.pyqtSlot(QtGui.QColor)
    def setAlternateTextColor(self, color):
        if isinstance(color, QtCore.Qt.GlobalColor):
            color = QtGui.QColor(color)
        elif not isinstance(color, QtGui.QColor):
            return
        self._alternateTextColor = color
        self._polishApp()

    def drawControl(self, element, option, painter, widget):
        if element == self.CE_PushButton:
            isAlternate = False
            if widget and isinstance(widget.parent(), QtWidgets.QDialogButtonBox):
                role = widget.parent().buttonRole(widget)
                if role in self._alternateRoles:
                    isAlternate = True
            elif widget.property('alternateColor'):
                isAlternate = True

            if isAlternate:
                color = self.alternateButtonColor()
                textColor = self.alternateTextColor()
            else:
                color = self.defaultButtonColor()
                textColor = self.defaultTextColor()

            if not option.state & self.State_Enabled:
                color.setAlpha(color.alpha() * .75)
                textColor.setAlpha(textColor.alpha() * .75)

            # switch the existing palette with a new one created from it;
            # this shouldn't be necessary, but better safe than sorry
            oldPalette = option.palette
            palette = QtGui.QPalette(oldPalette)
            palette.setColor(palette.ButtonText, textColor)
            # some styles use WindowText for flat buttons
            palette.setColor(palette.WindowText, textColor)
            option.palette = palette

            # colors that are almost black are not very affected by "lighter"
            if color.value() < 32:
                lightColor = QtGui.QColor(48, 48, 48, color.alpha())
            else:
                lightColor = color.lighter(115)
            darkColor = color.darker()
            if option.state & self.State_MouseOver:
                # colors that are almost black are not very affected by "lighter"
                bgColor = lightColor
                lighterColor = lightColor.lighter(115)
                darkerColor = darkColor.darker(115)
            else:
                bgColor = color
                lighterColor = lightColor
                darkerColor = darkColor
            if option.state & self.State_Raised and not option.state & self.State_On:
                topLeftPen = QtGui.QPen(lighterColor)
                bottomRightPen = QtGui.QPen(darkerColor)
            elif option.state & (self.State_On | self.State_Sunken):
                if option.state & self.State_On:
                    bgColor = bgColor.darker()
                else:
                    bgColor = bgColor.darker(125)
                topLeftPen = QtGui.QPen(darkColor)
                bottomRightPen = QtGui.QPen(lighterColor)
            else:
                topLeftPen = bottomRightPen = QtGui.QPen(bgColor)

            painter.save()
            painter.setRenderHints(painter.Antialiasing)
            painter.translate(.5, .5)
            rect = option.rect.adjusted(0, 0, -1, -1)
            painter.setBrush(bgColor)
            painter.setPen(QtCore.Qt.NoPen)
            painter.drawRoundedRect(rect, 2, 2)

            if topLeftPen != bottomRightPen:
                roundRect = QtCore.QRectF(0, 0, 4, 4)
                painter.setBrush(QtCore.Qt.NoBrush)

                # the top and left borders
                tlPath = QtGui.QPainterPath()
                tlPath.arcMoveTo(roundRect.translated(0, rect.height() - 4), 225)
                tlPath.arcTo(roundRect.translated(0, rect.height() - 4), 225, -45)
                tlPath.arcTo(roundRect, 180, -90)
                tlPath.arcTo(roundRect.translated(rect.width() - 4, 0), 90, -45)
                painter.setPen(topLeftPen)
                painter.drawPath(tlPath)

                # the bottom and right borders
                brPath = QtGui.QPainterPath(tlPath.currentPosition())
                brPath.arcTo(roundRect.translated(rect.width() - 4, 0), 45, -45)
                brPath.arcTo(
                    roundRect.translated(rect.width() - 4, rect.height() - 4), 0, -90)
                brPath.arcTo(
                    roundRect.translated(0, rect.height() - 4), 270, -45)
                painter.setPen(bottomRightPen)
                painter.drawPath(brPath)


            if option.state & self.State_HasFocus:
                focusColor = QtGui.QColor(textColor).darker()
                focusColor.setAlpha(focusColor.alpha() * .75)
                painter.setPen(focusColor)
                painter.setBrush(QtCore.Qt.NoBrush)
                painter.drawRoundedRect(rect.adjusted(2, 2, -2, -2), 2, 2)

            painter.setFont(self._buttonFont)
            oldMetrics = option.fontMetrics
            option.fontMetrics = self._buttonFontMetrics
            self.drawControl(self.CE_PushButtonLabel, option, painter, widget)
            painter.restore()

            # restore the original font metrics and palette
            option.fontMetrics = oldMetrics
            option.palette = oldPalette
            return

        super().drawControl(element, option, painter, widget)

    def sizeFromContents(self, contentsType, option, size, widget=None):
        if contentsType == self.CT_PushButton:
            if option.text:
                textSize = option.fontMetrics.size(
                    QtCore.Qt.TextShowMnemonic, option.text)
                baseWidth = size.width() - textSize.width()
                baseHeight = size.height() - textSize.height()
                text = option.text
            else:
                baseWidth = size.width()
                baseHeight = size.height()
                text = 'XXXX' if not option.icon else ''
            buttonTextSize = self._buttonFontMetrics.size(
                QtCore.Qt.TextShowMnemonic, text)
            if not widget or widget.font() != QtWidgets.QApplication.font():
                buttonTextSize = buttonTextSize.expandedTo(
                    QtWidgets.QApplication.fontMetrics().size(
                        QtCore.Qt.TextShowMnemonic, text))
            margin = self.pixelMetric(self.PM_ButtonMargin, option, widget)
            newSize = QtCore.QSize(
                buttonTextSize.width() + baseWidth + margin * 2, 
                buttonTextSize.height() + baseHeight + margin)
            return newSize.expandedTo(
                super().sizeFromContents(contentsType, option, size, widget))
        return super().sizeFromContents(contentsType, option, size, widget)


app = QtWidgets.QApplication(sys.argv)
app.setStyle(ColorButtonStyle())
# ...

从这一点开始,您还可以添加其他属性以更好地控制样式,例如圆角矩形的半径(只需在边框图形中用变量替换每个“4”,并使用一半的变量绘制背景):

        painter.drawRoundedRect(rect, self._radius / 2, self._radius / 2)

        if topLeftPen != bottomRightPen:
            roundRect = QtCore.QRectF(0, 0, self._radius, self._radius)
            painter.setBrush(QtCore.Qt.NoBrush)

            # the top and left borders
            tlPath = QtGui.QPainterPath()
            tlPath.arcMoveTo(roundRect.translated(
                0, rect.height() - self._radius), 225)
            tlPath.arcTo(roundRect.translated(
                   0, rect.height() - self._radius), 225, -45)

相关问题 更多 >