PyQt5 Linux Mint Cinnamon нативные кнопки «удалить», «применить» - PullRequest
0 голосов
/ 12 марта 2020

Я пытаюсь создать приложение pyqt5 на Linux mint 19 (cinnamon). У меня есть кнопки «удалить» и «применить», но я хочу, чтобы они выглядели более актуально. Как на этой картинке:

родной Linux мятные кнопки

больше

Это Родной вид кнопок с назначением или удалением роли на linux mint, и я хочу создать подобные кнопки в своем приложении, но я не нашел способа сделать это.

Кажется, что windows и ma c имеют модули qtmacextras и qtwinextras для таких вещей. Linux имеют какой-то qtx11extras, но этот модуль не обеспечивает такую ​​функциональность.

Ответы [ 2 ]

1 голос
/ 13 марта 2020

Вы можете использовать таблицу стилей, чтобы создать ее по своему вкусу.

Вот отдельный пример. Конечно, вы должны адаптировать его к вашим предпочтительным цветам и размерам. Но это уже должно послужить отправной точкой.

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

0 голосов
/ 13 марта 2020

Несмотря на то, что уже предоставленное решение работает отлично, оно не является полностью общесоюзным, особенно если вы хотите контролировать внешний вид кнопок диалогов.

Даже если вы применяете таблицу стилей к приложению, вам потребуется чтобы гарантировать правильную установку селекторов в родителях: в другом ответе, например, установка свойства фона без селекторов очистит наследование таблицы стилей приложения от детей.

Предполагается, что таблица стилей установлена ​​для приложения, и все Остальные наборы стилей тщательно написаны, проблема связана с диалогом windows.

Если диалоговое окно создается вручную, можно установить альтернативные цвета, указав таблицу стилей для каждой кнопки, для которой требуется альтернативный цвет, но это невозможно для тех, которые созданы с функциями stati c.

В этом случае единственная возможность - использовать QProxyStyle. Ниже приведена возможная реализация, которая также позволяет устанавливать собственные цвета и шрифты и автоматически устанавливает альтернативный цвет для «отрицательных» ролей полей диалоговых кнопок (отмена, игнорирование и т. Д. c).

В этом пример Я только что применил стиль к приложению, но окно сообщения создается с использованием функции information() stati c. Кнопка «Альтернатива» устанавливается вручную с помощью пользовательского свойства: 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)
...