【MayaPySide】ちょっとおしゃれなUIメソッド【3日目】

QSS,Tips,StyleSheet,PySide,Tutorial,PySide2,Python,Qt,Maya

現在記事の修正中です

こんにちはMayaPython Advent Calendar 2017の15日目の記事です
全記事一覧です

【MayaPySide】ちょっとおしゃれなUIメソッド【1日目】
【MayaPySide】ちょっとおしゃれなUIメソッド【2日目】
【MayaPySide】ちょっとおしゃれなUIメソッド【3日目】

はい!本日はなんと特別なことがあります!
そう、それは

誕生日!!!

たくさんのプレゼント!!待ってます!!!
5000兆円とか!!!
ということで、ちょっとおしゃれなUIメソッドの三日目です。最終となります。
かなり量が多いので頑張っていきましょう!

from functools import cached_property
from typing import Any

from maya import OpenMayaUI
from PySide2 import QtCore, QtGui, QtWidgets
from shiboken2 import wrapInstance

class ClosePushButton(QtWidgets.QPushButton):
    closed: QtCore.Signal = QtCore.Signal()

    def __init__(self, parent: QtWidgets.QWidget = None) -> None:
        super().__init__(parent)
        self.clicked.connect(self._closed)

    def _closed(self) -> None:
        self.closed.emit()

class CornerGrip(QtWidgets.QWidget):
    cursor_factory = {
        QtCore.Qt.LeftEdge: QtCore.Qt.SizeHorCursor,
        QtCore.Qt.TopEdge: QtCore.Qt.SizeVerCursor,
        QtCore.Qt.RightEdge: QtCore.Qt.SizeHorCursor,
        QtCore.Qt.BottomEdge: QtCore.Qt.SizeVerCursor,
    }
    resizeFunc: Any
    mousePosition: QtCore.QPoint = None

    def __init__(
        self, parent: QtWidgets.QWidget = None, edge: QtCore.Qt.SizeAllCursor = None
    ):
        super().__init__(parent)

        cursor = self.cursor_factory.get(edge)
        self.setCursor(cursor)
        self.setResizeFunction(edge)

    def setResizeFunction(self, edge) -> None:
        if edge == QtCore.Qt.LeftEdge:
            self.resizeFunc = self.resizeLeft
        elif edge == QtCore.Qt.TopEdge:
            self.resizeFunc = self.resizeTop
        elif edge == QtCore.Qt.RightEdge:
            self.resizeFunc = self.resizeRight
        elif edge == QtCore.Qt.BottomEdge:
            self.resizeFunc = self.resizeBottom

    @cached_property
    def window(self) -> QtWidgets.QWidget:
        return super().window()

    def resizeLeft(self, delta):
        width = max(self.window.minimumWidth(), self.window.width() - delta.x())
        geo = self.window.geometry()
        geo.setLeft(geo.right() - width)
        self.window.setGeometry(geo)

    def resizeTop(self, delta):
        height = max(self.window.minimumHeight(), self.window.height() - delta.y())
        geo = self.window.geometry()
        geo.setTop(geo.bottom() - height)
        self.window.setGeometry(geo)

    def resizeRight(self, delta):
        width = max(self.window.minimumWidth(), self.window.width() + delta.x())
        self.window.resize(width, self.window.height())

    def resizeBottom(self, delta):
        height = max(self.window.minimumHeight(), self.window.height() + delta.y())
        self.window.resize(self.window.width(), height)

    def mousePressEvent(self, event: QtGui.QMouseEvent) -> Any:
        if event.button() == QtCore.Qt.LeftButton:
            self.mousePosition = event.pos()

    def mouseMoveEvent(self, event: QtGui.QMouseEvent) -> Any:
        if self.mousePosition:
            delta = event.pos() - self.mousePosition
            self.resizeFunc(delta)

    def mouseReleaseEvent(self, event: QtGui.QMouseEvent) -> Any:
        self.mousePosition = None

class FrameGrips:
    topLeft: QtWidgets.QSizeGrip
    topRight: QtWidgets.QSizeGrip
    bottomRight: QtWidgets.QSizeGrip
    buttomLeft: QtWidgets.QSizeGrip
    left: CornerGrip
    top: CornerGrip
    right: CornerGrip
    bottom: CornerGrip
    parent: QtWidgets.QWidget
    __qss: str = "background-color: transparent;"

    def __init__(self, parent: QtWidgets.QWidget = None) -> None:
        self.parent = parent
        self.topLeft = QtWidgets.QSizeGrip(parent)
        self.topRight = QtWidgets.QSizeGrip(parent)
        self.bottomRight = QtWidgets.QSizeGrip(parent)
        self.buttomLeft = QtWidgets.QSizeGrip(parent)
        self.left = CornerGrip(parent, edge=QtCore.Qt.LeftEdge)
        self.top = CornerGrip(parent, edge=QtCore.Qt.TopEdge)
        self.right = CornerGrip(parent, edge=QtCore.Qt.RightEdge)
        self.bottom = CornerGrip(parent, edge=QtCore.Qt.BottomEdge)

        self.topLeft.setStyleSheet(self.__qss)
        self.topRight.setStyleSheet(self.__qss)
        self.bottomRight.setStyleSheet(self.__qss)
        self.buttomLeft.setStyleSheet(self.__qss)
        self.left.setStyleSheet(self.__qss)
        self.top.setStyleSheet(self.__qss)
        self.right.setStyleSheet(self.__qss)
        self.bottom.setStyleSheet(self.__qss)

class FramelessMainWindow(QtWidgets.QMainWindow):
    pos: QtCore.QPoint
    radius: int = 15
    backgroundColorCode: str = "#333333"
    backgroundColor = QtGui.QColor(backgroundColorCode)

    gradient: QtGui.QLinearGradient = QtGui.QLinearGradient()
    gradient.setColorAt(0.0, "#54354e")
    gradient.setColorAt(1.0, "#6a86c7")
    isDrag: bool = False
    isPress: bool = False
    cornerLength = 10
    frameGrips: FrameGrips

    def __init__(self, parent: QtWidgets.QWidget = None) -> None:
        super().__init__(parent)
        self.setAutoFillBackground(True)
        self.setAttribute(QtCore.Qt.WA_TranslucentBackground)
        self.installEventFilter(self)
        self.setWindowFlags(QtCore.Qt.FramelessWindowHint)
        self.frameGrips = FrameGrips(self)
        self.setContentsMargins(*[self.gripSize] * 4)

    @property
    def gripSize(self):
        return self.cornerLength

    def setGripSize(self, size):
        if size == self.cornerLength:
            return
        self.cornerLength = max(2, size)
        self.setContentsMargins(*[self.gripSize] * 4)
        self.updateGrips()

    def updateGrips(self) -> None:
        outRect = self.rect()
        # an "inner" rect used for reference to set the geometries of size grips
        inRect = outRect.adjusted(
            self.gripSize, self.gripSize, -self.gripSize, -self.gripSize
        )
        # top left
        self.frameGrips.topLeft.setGeometry(
            QtCore.QRect(outRect.topLeft(), inRect.topLeft())
        )
        # top right
        self.frameGrips.topRight.setGeometry(
            QtCore.QRect(outRect.topRight(), inRect.topRight()).normalized()
        )
        # bottom right
        self.frameGrips.bottomRight.setGeometry(
            QtCore.QRect(inRect.bottomRight(), outRect.bottomRight())
        )
        # bottom left
        self.frameGrips.buttomLeft.setGeometry(
            QtCore.QRect(outRect.bottomLeft(), inRect.bottomLeft()).normalized()
        )

        # left edge
        self.frameGrips.left.setGeometry(
            0, inRect.top(), self.gripSize, inRect.height()
        )
        # top edge
        self.frameGrips.top.setGeometry(inRect.left(), 0, inRect.width(), self.gripSize)
        # right edge
        self.frameGrips.right.setGeometry(
            inRect.left() + inRect.width(), inRect.top(), self.gripSize, inRect.height()
        )
        # bottom edge
        self.frameGrips.bottom.setGeometry(
            self.gripSize, inRect.top() + inRect.height(), inRect.width(), self.gripSize
        )

    def resizeEvent(self, event):
        QtWidgets.QMainWindow.resizeEvent(self, event)
        self.updateGrips()

    def paintEvent(self, event: QtGui.QMouseEvent) -> Any:
        painter = QtGui.QPainter(self)
        painter.setRenderHint(QtGui.QPainter.Antialiasing)
        path = QtGui.QPainterPath()
        self.gradient.setStart(QtCore.QRectF(self.rect()).topLeft())
        self.gradient.setFinalStop(QtCore.QRectF(self.rect()).topRight())
        brush = QtGui.QBrush(self.gradient)
        painter.setBrush(brush)
        path.addRoundedRect(0, 0, self.width(), self.height(), self.radius, self.radius)
        painter.setClipPath(path)
        painter.fillPath(path, painter.brush())
        return super().paintEvent(event)

    def mouseReleaseEvent(self, event: QtGui.QMouseEvent) -> None:
        self.isPress = False
        self.pressPos = event.pos()
        super().mouseReleaseEvent(event)

    def mousePressEvent(self, event: QtGui.QMouseEvent) -> None:
        self.isPress = True
        self.pressPos = event.pos()

        super().mousePressEvent(event)

    def mouseMoveEvent(self, event: QtGui.QMouseEvent) -> None:
        if self.isPress or self.isDrag:
            self.move(
                event.globalX() - self.pressPos.x(), event.globalY() - self.pressPos.y()
            )
        super().mouseMoveEvent(event)

    def eventFilter(self, obj: QtCore.QObject, event: QtCore.QEvent) -> Any:
        if event.type() == QtCore.QEvent.Type.MouseButtonPress:
            self.isDrag = True
            return super().eventFilter(obj, event)
        if event.type() == QtCore.QEvent.Type.MouseButtonRelease:
            self.isDrag = False
            return super().eventFilter(obj, event)

class ExampleCentralWidget(QtWidgets.QWidget):
    def __init__(self, parent: QtWidgets.QWidget = None) -> None:
        super(ExampleCentralWidget, self).__init__(parent)
        self.closePushButton = ClosePushButton(self)
        self.closePushButton.setText("Close")
        self.mainLayout = QtWidgets.QVBoxLayout(self)
        self.mainLayout.addWidget(self.closePushButton)
        self.mainLayout.addStretch(True)
        self.setLayout(self.mainLayout)

class FramelessMainWindow(QtWidgets.QMainWindow):
    pos: QtCore.QPoint
    radius: int = 15
    backgroundColorCode: str = "#333333"
    backgroundColor = QtGui.QColor(backgroundColorCode)

    gradient: QtGui.QLinearGradient = QtGui.QLinearGradient()
    gradient.setColorAt(0.0, "#54354e")
    gradient.setColorAt(1.0, "#6a86c7")
    isDrag: bool = False
    isPress: bool = False
    cornerLength = 10
    frameGrips: FrameGrips

    def __init__(self, parent: QtWidgets.QWidget = None) -> None:
        super().__init__(parent)
        self.setAutoFillBackground(True)
        self.setAttribute(QtCore.Qt.WA_TranslucentBackground)
        self.installEventFilter(self)
        self.setWindowFlags(QtCore.Qt.FramelessWindowHint)
        self.frameGrips = FrameGrips(self)
        self.setContentsMargins(*[self.gripSize] * 4)

    @property
    def gripSize(self):
        return self.cornerLength

    def setGripSize(self, size):
        if size == self.cornerLength:
            return
        self.cornerLength = max(2, size)
        self.setContentsMargins(*[self.gripSize] * 4)
        self.updateGrips()

    def updateGrips(self) -> None:
        outRect = self.rect()
        # an "inner" rect used for reference to set the geometries of size grips
        inRect = outRect.adjusted(
            self.gripSize, self.gripSize, -self.gripSize, -self.gripSize
        )
        # top left
        self.frameGrips.topLeft.setGeometry(
            QtCore.QRect(outRect.topLeft(), inRect.topLeft())
        )
        # top right
        self.frameGrips.topRight.setGeometry(
            QtCore.QRect(outRect.topRight(), inRect.topRight()).normalized()
        )
        # bottom right
        self.frameGrips.bottomRight.setGeometry(
            QtCore.QRect(inRect.bottomRight(), outRect.bottomRight())
        )
        # bottom left
        self.frameGrips.buttomLeft.setGeometry(
            QtCore.QRect(outRect.bottomLeft(), inRect.bottomLeft()).normalized()
        )

        # left edge
        self.frameGrips.left.setGeometry(
            0, inRect.top(), self.gripSize, inRect.height()
        )
        # top edge
        self.frameGrips.top.setGeometry(inRect.left(), 0, inRect.width(), self.gripSize)
        # right edge
        self.frameGrips.right.setGeometry(
            inRect.left() + inRect.width(), inRect.top(), self.gripSize, inRect.height()
        )
        # bottom edge
        self.frameGrips.bottom.setGeometry(
            self.gripSize, inRect.top() + inRect.height(), inRect.width(), self.gripSize
        )

    def resizeEvent(self, event):
        QtWidgets.QMainWindow.resizeEvent(self, event)
        self.updateGrips()

    def paintEvent(self, event: QtGui.QMouseEvent) -> Any:
        painter = QtGui.QPainter(self)
        painter.setRenderHint(QtGui.QPainter.Antialiasing)
        path = QtGui.QPainterPath()
        self.gradient.setStart(QtCore.QRectF(self.rect()).topLeft())
        self.gradient.setFinalStop(QtCore.QRectF(self.rect()).topRight())
        brush = QtGui.QBrush(self.gradient)
        painter.setBrush(brush)
        path.addRoundedRect(0, 0, self.width(), self.height(), self.radius, self.radius)
        painter.setClipPath(path)
        painter.fillPath(path, painter.brush())
        return super().paintEvent(event)

    def mouseReleaseEvent(self, event: QtGui.QMouseEvent) -> None:
        self.isPress = False
        self.pressPos = event.pos()
        super().mouseReleaseEvent(event)

    def mousePressEvent(self, event: QtGui.QMouseEvent) -> None:
        self.isPress = True
        self.pressPos = event.pos()

        super().mousePressEvent(event)

    def mouseMoveEvent(self, event: QtGui.QMouseEvent) -> None:
        if self.isPress or self.isDrag:
            self.move(
                event.globalX() - self.pressPos.x(), event.globalY() - self.pressPos.y()
            )
        super().mouseMoveEvent(event)

    def eventFilter(self, obj: QtCore.QObject, event: QtCore.QEvent) -> Any:
        if event.type() == QtCore.QEvent.Type.MouseButtonPress:
            self.isDrag = True
            return super().eventFilter(obj, event)
        if event.type() == QtCore.QEvent.Type.MouseButtonRelease:
            self.isDrag = False
            return super().eventFilter(obj, event)

class Example(FramelessMainWindow):
    timer: QtCore.QTimer = QtCore.QTimer()
    fade: QtCore.QPropertyAnimation

    def __init__(self, parent: QtWidgets.QWidget = None) -> None:
        super(Example, self).__init__(parent)
        self.__initUI()
        self.setWidget()

    def __initUI(self) -> None:
        self.installEventFilter(self)
        self.setWindowOpacity(0.85)
        self.setGeometry(300, 300, 500, 350)
        self.setWindowFlags(QtCore.Qt.Window | QtCore.Qt.FramelessWindowHint)

    def setWidget(self) -> None:
        self.setCentralWidget(ExampleCentralWidget(self))
        self.centralWidget().ClosePushButton.closed.connect(self.close)

    def close(self) -> None:
        self.timer.setInterval(600)
        self.timer.timeout.connect(super().close)
        self.timer.start()
        self.fade = QtCore.QPropertyAnimation(self, b"windowOpacity")
        self.fade.setStartValue(0.85)
        self.fade.setEndValue(0.0)
        self.fade.setKeyValueAt(0.5, 0.0)
        self.fade.setEasingCurve(QtCore.QEasingCurve.InOutCubic)
        self.fade.setDuration(600)
        self.fade.start()

def main() -> None:
    mayaMainWindow = wrapInstance(
        int(OpenMayaUI.MQtUtil.mainWindow()), QtWidgets.QMainWindow
    )
    ex = Example(mayaMainWindow)
    ex.show()

# main()

上に記述している前回までのコードをコピーして使用してください。

下の表のようなフォルダファイル構成を作っていきます。

C:\Users\<ユーザー名>\Documents\maya\scripts\unpyside/
├── __init__.py/
├── ac2017/
|   ├── __init__.py
|   ├── example.py
|   └── resource/
|       ├── < imageName >.png
|       └── < imageName >.png

Index直下には今まで作成してきたソースコードをexample.pyという名前で保存します。
一番最後の行にあるmain()は消しておきます。一度うまくmaya読み込めるか、以下のコードをスクリプトエディターで実行してみましょう

import unpyside.ac2017.example
unpyside.ac2017.example.main()

import unpyside.ac2017.exampleの次の行にfrom importlib import reloadと`reload(index.example)を記述するとモジュールをリロードできます

from importlib import reload
import unpyside.ac2017.example
reload(unpyside.ac2017.example)
unpyside.ac2017.example.main()

さて、今回の記事で作っていくものはメインのスライダー
そして6つのボタン、closeボタンとなります。
それらを作っていくにはまず必要なイメージ画像を用意します

今回、私が使用している画像のサイズはこのような構成になっています

画像の種類サイズ
Closeボタン用の画像12x12px
スライダー用の画像24x24px
その他6種類のボタン32x32px

6種類のボタンのサイズが大きいサイズなのはあまり気にしないでください
もともと大きいサイズでボタンを構成していた名残です
また、データは基本pngを使用しています


このリンクからDLして使用してください

DLして解凍した画像をunpyside/ac2017/resourceに入れて下さい。
次にその画像をQRCを作成し.pyに変換して読み込んでいきます。

C:\Users\<ユーザー名>\Documents\maya\scripts\unpyside/
├── __init__.py/
├── ac2017/
|   ├── __init__.py
|   ├── example.py
|   ├── create_qrc.py
|   ├── create_qrc.bat
|   └── resource/
|       ├── < imageName >.png
|       └── < imageName >.png

create_qrc.pyとcreate_qrc.batをexample.pyと同じ階層に作成して、以下のコードのようにしてください

create_qrc.py

import sys
from pathlib import Path

PREFIX = "/resource/"

def searchFiles(root: Path) -> list[Path]:
    result = []
    for iter in root.iterdir():
        if iter.suffix in [".png", ".jpg", ".svg"]:
            result.append(iter)
    return result

def createCode(root: Path) -> str:
    code = f''
    for file in searchFiles(root):
        code += f"{file}"
    code += ""
    return code

def main():
    print(sys.argv)
    root = Path(sys.argv[1])
    qrc = root / "resource.qrc"

    with open(qrc, "w") as f:
        f.write(createCode(root))

if __name__ == "__main__":
    main()

MAYADIRは自分の使用しているMayaのバージョンに合わせてください
create_qrc.bat

@echo off
chcp 65001 > nul
cd /d %~dp0
setlocal enabledelayedexpansion

set EXPORTPYTHON=%1\resource.py
set QRCPATH=%1\resource.qrc
set MAYADIR="C:\Program Files\Autodesk\Maya2023\bin\"
%MAYADIR%mayapy.exe %~dp0create_qrc.py %1
%MAYADIR%pyside2-rcc.exe -o %EXPORTPYTHON% %QRCPATH%
move %EXPORTPYTHON% %~dp1

create_qrc.batにresourceフォルダドラックアンドドロップし、しばらくするとresource.pyが作られます

C:\Users\<ユーザー名>\Documents\maya\scripts\unpyside/
├── __init__.py/
├── ac2017/
|   ├── __init__.py
|   ├── example.py
|   ├── create_qrc.py
|   ├── create_qrc.bat
|   ├── resource.py
|   └── resource/
|       ├── < imageName >.png
|       └── < imageName >.png

resouceの準備ができたら、resource.pyをimportします


from functools import cached_property
from typing import Any

from maya import OpenMayaUI
from PySide2 import QtCore, QtGui, QtWidgets
from shiboken2 import wrapInstance
from . import resource

class ClosePushButton(QtWidgets.QPushButton):
    closed: QtCore.Signal = QtCore.Signal()

    def __init__(self, parent: QtWidgets.QWidget = None) -> None:
        super().__init__(parent)
        self.clicked.connect(self._closed)
        self.setIcon(QtGui.QIcon(":/resource/close.png"))
        self.setIconSize(QtCore.QSize(14, 14))

    ...

class CornerGrip(QtWidgets.QWidget):
    ...

class FrameGrips:
    ...

class ExampleCentralWidget(QtWidgets.QWidget):
    def __init__(self, parent: QtWidgets.QWidget = None) -> None:
        super(ExampleCentralWidget, self).__init__(parent)
        self.closePushButton = ClosePushButton(self)
        # self.closePushButton.setText("Close")
        self.mainLayout = QtWidgets.QVBoxLayout(self)
        self.mainLayout.addWidget(self.closePushButton)
        self.mainLayout.addStretch(True)
        self.setLayout(self.mainLayout)

class FramelessMainWindow(QtWidgets.QMainWindow):
    ...

class Example(FramelessMainWindow):
    ...

def main() -> None:
    ...

from . import resourceresource.pyを読み込みます
ClosePushButtonがインスタンス化する際に、アイコンを設定するようにsetIcon(QtGui.QIcon(":/resource/close.png"))setIconSize(QtCore.QSize(14, 14))を設定しています。
下の画像のように画像がボタンに表示されていればうまくできています。

ここで完成図を改めてみてみます

ざっくり、まとめると以下のような構成になっています

  • 上部に文字と閉じるボタン
  • 中心部にスライダー
  • 下部にボタンを配置
    QGridLayoutを使うことももちろん可能なのですがメンテナンスがめんどくさいので、上中下をQVBoxLayoutを使用して一つにまとめ、それぞれの行はQHBoxLayoutを使用するという方法で構成していきます。

Qt Designerを使ってUIを構築します

ExampleCentralWidget.ui/
├── Form(QHBoxLayout)/
|   ├── horizontalLayout_menu
|   |   ├── horizontalSpacer
|   |   └── label_title
|   ├── horizontalLayout_menu
|   ├── horizontalLayout_silider
|   ├── verticalSpacer_bottom
|   └── verticalSpacer_top/









from __future__ import annotations

from functools import cached_property
from pathlib import Path
from typing import Any, Final, Type

from maya import OpenMayaUI
from PySide2 import QtCore, QtGui, QtWidgets
from PySide2.QtUiTools import loadUiType
from shiboken2 import wrapInstance

from . import resource

ROOT_DIR: Final = Path(__file__).parent
EXAMPLECENTRALWIDEGET_UI_PATH: Final = ROOT_DIR / "ui" / "ExampleCentralWidget.ui"

def loadWidgetUiType(
    file: Path | str,
) -> tuple[Type[QtWidgets.QWidget], Type[QtWidgets.QWidget]]:
    if isinstance(file, Path):
        file = file.as_posix()
    return loadUiType(file)

_, base = loadWidgetUiType(EXAMPLECENTRALWIDEGET_UI_PATH)

class ClosePushButton(QtWidgets.QPushButton):
    closed: QtCore.Signal = QtCore.Signal()

    def __init__(self, parent: QtWidgets.QWidget = None) -> None:
        super().__init__(parent)
        self.clicked.connect(self._closed)
        self.setIcon(QtGui.QIcon(":/resource/close.png"))
        self.setIconSize(QtCore.QSize(14, 14))

    def _closed(self) -> None:
        self.closed.emit()

class ExampleCentralWidget(_, base):
    label_title: QtWidgets.QLabel
    horizontalLayout_menu: QtWidgets.QHBoxLayout
    horizontalLayout_silider: QtWidgets.QHBoxLayout
    horizontalLayout_buttons: QtWidgets.QHBoxLayout

    def __init__(self, parent: QtWidgets.QWidget = None) -> None:
        super().__init__(parent)
        self.setupUi(self)
        self.__init_buttons()

    def __init_buttons(self):
        self.closePushButton = ClosePushButton(self)
        self.horizontalLayout_menu.addWidget(self.closePushButton)

        for num in range(5):
            btn = QtWidgets.QPushButton(self)
            btn.setIcon(QtGui.QIcon(f":/resource/Btn_({num}).png"))
            btn.setIconSize(QtCore.QSize(14, 14))
            self.horizontalLayout_buttons.addWidget(btn)

class CornerGrip(QtWidgets.QWidget):
    cursor_factory = {
        QtCore.Qt.LeftEdge: QtCore.Qt.SizeHorCursor,
        QtCore.Qt.TopEdge: QtCore.Qt.SizeVerCursor,
        QtCore.Qt.RightEdge: QtCore.Qt.SizeHorCursor,
        QtCore.Qt.BottomEdge: QtCore.Qt.SizeVerCursor,
    }
    resizeFunc: Any
    mousePosition: QtCore.QPoint = None

    def __init__(
        self, parent: QtWidgets.QWidget = None, edge: QtCore.Qt.SizeAllCursor = None
    ):
        super().__init__(parent)

        cursor = self.cursor_factory.get(edge)
        self.setCursor(cursor)
        self.setResizeFunction(edge)

    def setResizeFunction(self, edge) -> None:
        if edge == QtCore.Qt.LeftEdge:
            self.resizeFunc = self.resizeLeft
        elif edge == QtCore.Qt.TopEdge:
            self.resizeFunc = self.resizeTop
        elif edge == QtCore.Qt.RightEdge:
            self.resizeFunc = self.resizeRight
        elif edge == QtCore.Qt.BottomEdge:
            self.resizeFunc = self.resizeBottom

    @cached_property
    def window(self) -> QtWidgets.QWidget:
        return super().window()

    def resizeLeft(self, delta):
        width = max(self.window.minimumWidth(), self.window.width() - delta.x())
        geo = self.window.geometry()
        geo.setLeft(geo.right() - width)
        self.window.setGeometry(geo)

    def resizeTop(self, delta):
        height = max(self.window.minimumHeight(), self.window.height() - delta.y())
        geo = self.window.geometry()
        geo.setTop(geo.bottom() - height)
        self.window.setGeometry(geo)

    def resizeRight(self, delta):
        width = max(self.window.minimumWidth(), self.window.width() + delta.x())
        self.window.resize(width, self.window.height())

    def resizeBottom(self, delta):
        height = max(self.window.minimumHeight(), self.window.height() + delta.y())
        self.window.resize(self.window.width(), height)

    def mousePressEvent(self, event: QtGui.QMouseEvent) -> Any:
        if event.button() == QtCore.Qt.LeftButton:
            self.mousePosition = event.pos()

    def mouseMoveEvent(self, event: QtGui.QMouseEvent) -> Any:
        if self.mousePosition:
            delta = event.pos() - self.mousePosition
            self.resizeFunc(delta)

    def mouseReleaseEvent(self, event: QtGui.QMouseEvent) -> Any:
        self.mousePosition = None

class FrameGrips:
    topLeft: QtWidgets.QSizeGrip
    topRight: QtWidgets.QSizeGrip
    bottomRight: QtWidgets.QSizeGrip
    buttomLeft: QtWidgets.QSizeGrip
    left: CornerGrip
    top: CornerGrip
    right: CornerGrip
    bottom: CornerGrip
    parent: QtWidgets.QWidget
    __qss: str = "background-color: transparent;"

    def __init__(self, parent: QtWidgets.QWidget = None) -> None:
        self.parent = parent
        self.topLeft = QtWidgets.QSizeGrip(parent)
        self.topRight = QtWidgets.QSizeGrip(parent)
        self.bottomRight = QtWidgets.QSizeGrip(parent)
        self.buttomLeft = QtWidgets.QSizeGrip(parent)
        self.left = CornerGrip(parent, edge=QtCore.Qt.LeftEdge)
        self.top = CornerGrip(parent, edge=QtCore.Qt.TopEdge)
        self.right = CornerGrip(parent, edge=QtCore.Qt.RightEdge)
        self.bottom = CornerGrip(parent, edge=QtCore.Qt.BottomEdge)

        self.topLeft.setStyleSheet(self.__qss)
        self.topRight.setStyleSheet(self.__qss)
        self.bottomRight.setStyleSheet(self.__qss)
        self.buttomLeft.setStyleSheet(self.__qss)
        self.left.setStyleSheet(self.__qss)
        self.top.setStyleSheet(self.__qss)
        self.right.setStyleSheet(self.__qss)
        self.bottom.setStyleSheet(self.__qss)

class FramelessMainWindow(QtWidgets.QMainWindow):
    pos: QtCore.QPoint
    radius: int = 15
    backgroundColorCode: str = "#333333"
    backgroundColor = QtGui.QColor(backgroundColorCode)

    gradient: QtGui.QLinearGradient = QtGui.QLinearGradient()
    gradient.setColorAt(0.0, "#54354e")
    gradient.setColorAt(1.0, "#6a86c7")
    isDrag: bool = False
    isPress: bool = False
    cornerLength = 10
    frameGrips: FrameGrips

    def __init__(self, parent: QtWidgets.QWidget = None) -> None:
        super().__init__(parent)
        self.setAutoFillBackground(True)
        self.setAttribute(QtCore.Qt.WA_TranslucentBackground)
        self.installEventFilter(self)
        self.setWindowFlags(QtCore.Qt.FramelessWindowHint)
        self.frameGrips = FrameGrips(self)
        self.setContentsMargins(*[self.gripSize] * 4)

    @property
    def gripSize(self):
        return self.cornerLength

    def setGripSize(self, size):
        if size == self.cornerLength:
            return
        self.cornerLength = max(2, size)
        self.setContentsMargins(*[self.gripSize] * 4)
        self.updateGrips()

    def updateGrips(self) -> None:
        outRect = self.rect()
        # an "inner" rect used for reference to set the geometries of size grips
        inRect = outRect.adjusted(
            self.gripSize, self.gripSize, -self.gripSize, -self.gripSize
        )
        # top left
        self.frameGrips.topLeft.setGeometry(
            QtCore.QRect(outRect.topLeft(), inRect.topLeft())
        )
        # top right
        self.frameGrips.topRight.setGeometry(
            QtCore.QRect(outRect.topRight(), inRect.topRight()).normalized()
        )
        # bottom right
        self.frameGrips.bottomRight.setGeometry(
            QtCore.QRect(inRect.bottomRight(), outRect.bottomRight())
        )
        # bottom left
        self.frameGrips.buttomLeft.setGeometry(
            QtCore.QRect(outRect.bottomLeft(), inRect.bottomLeft()).normalized()
        )

        # left edge
        self.frameGrips.left.setGeometry(
            0, inRect.top(), self.gripSize, inRect.height()
        )
        # top edge
        self.frameGrips.top.setGeometry(inRect.left(), 0, inRect.width(), self.gripSize)
        # right edge
        self.frameGrips.right.setGeometry(
            inRect.left() + inRect.width(), inRect.top(), self.gripSize, inRect.height()
        )
        # bottom edge
        self.frameGrips.bottom.setGeometry(
            self.gripSize, inRect.top() + inRect.height(), inRect.width(), self.gripSize
        )

    def resizeEvent(self, event):
        QtWidgets.QMainWindow.resizeEvent(self, event)
        self.updateGrips()

    def paintEvent(self, event: QtGui.QMouseEvent) -> Any:
        painter = QtGui.QPainter(self)
        painter.setRenderHint(QtGui.QPainter.Antialiasing)
        path = QtGui.QPainterPath()
        self.gradient.setStart(QtCore.QRectF(self.rect()).topLeft())
        self.gradient.setFinalStop(QtCore.QRectF(self.rect()).topRight())
        brush = QtGui.QBrush(self.gradient)
        painter.setBrush(brush)
        path.addRoundedRect(0, 0, self.width(), self.height(), self.radius, self.radius)
        painter.setClipPath(path)
        painter.fillPath(path, painter.brush())
        return super().paintEvent(event)

    def mouseReleaseEvent(self, event: QtGui.QMouseEvent) -> None:
        self.isPress = False
        self.pressPos = event.pos()
        super().mouseReleaseEvent(event)

    def mousePressEvent(self, event: QtGui.QMouseEvent) -> None:
        self.isPress = True
        self.pressPos = event.pos()

        super().mousePressEvent(event)

    def mouseMoveEvent(self, event: QtGui.QMouseEvent) -> None:
        if self.isPress or self.isDrag:
            self.move(
                event.globalX() - self.pressPos.x(), event.globalY() - self.pressPos.y()
            )
        super().mouseMoveEvent(event)

    def eventFilter(self, obj: QtCore.QObject, event: QtCore.QEvent) -> Any:
        if event.type() == QtCore.QEvent.Type.MouseButtonPress:
            self.isDrag = True
            return super().eventFilter(obj, event)
        if event.type() == QtCore.QEvent.Type.MouseButtonRelease:
            self.isDrag = False
            return super().eventFilter(obj, event)

class Example(FramelessMainWindow):
    # btn: QtWidgets.QPushButton
    timer: QtCore.QTimer = QtCore.QTimer()
    fade: QtCore.QPropertyAnimation

    def __init__(self, parent: QtWidgets.QWidget = None) -> None:
        super(Example, self).__init__(parent)
        self.__initUI()
        self.setWidget()

    def __initUI(self) -> None:
        self.setMouseTracking(True)
        self.setWindowOpacity(0.85)
        self.setGeometry(300, 300, 500, 350)
        self.setWindowFlags(QtCore.Qt.Window | QtCore.Qt.FramelessWindowHint)

    def setWidget(self) -> None:
        self.setCentralWidget(ExampleCentralWidget(self))
        self.centralWidget().closePushButton.closed.connect(self.close)

    def close(self) -> None:
        self.timer.setInterval(600)
        self.timer.timeout.connect(super().close)
        self.timer.start()
        self.fade = QtCore.QPropertyAnimation(self, b"windowOpacity")
        self.fade.setStartValue(0.85)
        self.fade.setEndValue(0.0)
        self.fade.setKeyValueAt(0.5, 0.0)
        self.fade.setEasingCurve(QtCore.QEasingCurve.InOutCubic)
        self.fade.setDuration(600)
        self.fade.start()

def main() -> None:
    mayaMainWindow = wrapInstance(
        int(OpenMayaUI.MQtUtil.mainWindow()), QtWidgets.QMainWindow
    )
    ex = Example(mayaMainWindow)
    ex.show()

うまくいくと右クリックを押したときにどのボタンが押されたかをScriptEditorで結果が出てきます
さて、ここまででqssを使ったボタンの作成やclassを使って新しいボタンの作成をしてきました
次はこれまでしてきたことを応用してスライダーを作っていきます

実はこの画像にあるスライダーはQSliderだけを使って構成しているのではなく
組み合わせて表現しています
このスライダーの構成はQSliderQPushButtonQLineEditです。
配置イメージはこうなります


ではまずQPushuButtonの新しいクラスを設定します

QPushButton#ColorButton{
  border-style: solid;
  border-radius: 12px;
  border-width: 4px;
  border-color: #335b84;
  height: 16px;
}
class ColorButton(QPushButton):
    colorChanged = Signal(QColor)

    def __init__(self, *args, **kwargs):
        super(ColorButton, self).__init__(*args, **kwargs)
        self.setFlat(True)
        self.clicked.connect(self.showDialog)
        self.__color = QColor(255, 255, 255)
        self.__update()
        self.setObjectName(ColorButton)

    def showDialog(self):
        color = QColorDialog.getColor(self.__color)
        if not color.isValid():
            return

        self.__color = color
        self.__update()
        self.colorChanged.emit(self.__color)

    def __update(self):
        r, g, b, a = self.__color.getRgb()
        self.setStyleSheet(
            *{
                background:rgb(%s, %s, %s);
            }
             % (r, g, b)
            )

    def color(self):
        return self.__color

    def setColor(self, color):
        self.__color = color
        self.__update()
        self.colorChanged.emit(self.__color)

    def rgba(self):
        return self.__color.getRgb()

    def setRgba(self, r, g, b, a=255):
        self.__color.setRgb(r, g, b, a)
        self.__update()
        self.colorChanged.emit(self.__color)

    def hsv(self):
        return self.__color.getHsv()

    def setHsv(self, h, s, v, a=255):
        self.__color.setHsv(h, s, v, a)
        self.__update()
        self.colorChanged.emit(self.__color)

このColorButtonはスライダーが動いた際に色が変更したり、このボタンから色を選択することができます
実はKIWAMIDENさんのPySideでcolorSliderGrpモドキを作る方法をベースに作成しています
詳細はKIWAMIDENさんの記事を読むとよくわかるかと思います。
それぞれを設定していきます
以下のコードを追記してください

QSlider#Slider{
  border-color:  qlineargradient(
    x1: 0, y1: 0, x2: 2, y2: 0, stop: 0 #335b84, stop: 1 #347881
    );
  border-radius: 24px;
}
QSlider::groove:horizontal#Slider{
  height: 24px;
  border-radius: 24px;
  margin: 0 0.5px; 
}

QSlider::handle:horizontal#Slider{
  width: 24px;
  height: 24px;
  background-image: url(:/resource/handle.png)
}

QSlider::add-page:qlineargradient#Slider{
  background: #347881;
  border-top-right-radius: 12px;
  border-bottom-right-radius: 12px;
  border-top-left-radius: 0px;
  border-bottom-left-radius: 0px;
}

QSlider::sub-page:qlineargradient#Slider{
  background: #335b84;
  border-top-right-radius: 0px;
  border-bottom-right-radius: 0px;
  border-top-left-radius: 12px;
  border-bottom-left-radius: 12px;
}

QLineEdit#Line{
  font-family: Montserrat;
  font-style: Bold;
  background: #347881;
  border-color: #347881;
  border-radius: 12px; 
  color: #fff1e6;
  width: 58px;
  height: 24px;
}

class MainWidget(QWidget):
    SliderValueChanged = Signal(int)
    closed = Signal()

    def __init__(self):
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    def initUI(self):
        Mtl_Slider_P = 42
        self.clrBtn = ColorButton(self)
        self.clrBtn.setMinimumWidth(70)
        self.clrBtn.setMinimumHeight(10)
        self.clrBtn.colorChanged[QColor].connect(self.emitInt)

# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

        self.resultText = QLineEdit(self)
        self.resultText.setFrame(False)
        self.resultText.setAlignment(Qt.AlignRight)
        self.resultText.setObjectName('Line')
        self.resultText.setTextMargins(0, 0, 2, 1)

# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

        self.sld = QSlider(Qt.Horizontal, self)
        self.sld.valueChanged[int].connect(self.emitInt)
        self.sld.setRange(0, 100)
        self.sld.setValue(85)
        self.sld.setObjectName('Slider')

        self.resultText.move(194, Mtl_Slider_P)
        self.sld.setGeometry(36, Mtl_Slider_P, 188, 25)
        self.clrBtn.move(6, Mtl_Slider_P)

    def emitInt(self, value):
        try:
            value = int(round(value.value() / 2.55))
            colorsvalue = value
        except:
            pass

        self.resultText.setText(%s % (self.sld.value()))
        value = int(value * 2.55)
        sender = self.sender()
        if sender == self.clrBtn:
            color = self.clrBtn.color()
            self.sld.blockSignals(True)
            self.sld.setValue(color.value())
            self.sld.blockSignals(False)
            self.resultText.setText(%s % (colorsvalue))
            self.sld.setValue(colorsvalue)
        elif sender == self.sld:
            hsv = self.clrBtn.hsv()
            self.clrBtn.blockSignals(True)
            self.clrBtn.setHsv(hsv[0], hsv[1], value)
            self.clrBtn.blockSignals(False)
        return self.sld.value()

    def output(self):
        try:
            num = int(self.resultText.text())
            return num
        except:
            return Text wasn't numeral.

emitInt(self)
スライダーの値を変更し、ボタンの色を変更したりする機能です
output(self)
今現在のテキスト返す機能です
PushBtnにそれぞれ出力できるようにoutputをセットしていきます

    def push_right(self):
        sender = self.sender()
        if sender is self.Mtl_Btns[0]:
            print Mtl_Btns[0] -right\nValue: %s % self.output()
        elif sender is self.Mtl_Btns[1]:
            print Mtl_Btns[1] -right\nValue: %s % self.output()
        elif sender is self.Mtl_Btns[2]:
            print Mtl_Btns[2] -right\nValue: %s % self.output()
        elif sender is self.Mtl_Btns[3]:
            print Mtl_Btns[3] -right\nValue: %s % self.output()
        elif sender is self.Mtl_Btns[4]:
            print Mtl_Btns[4] -right\nValue: %s % self.output()
        elif sender is self.Mtl_Btns[5]:
            print Mtl_Btns[5] -right\nValue: %s % self.output()

    def push_left(self):
        sender = self.sender()
        if sender is self.Mtl_Btns[0]:
            print Mtl_Btns[0] -left\nValue: %s % self.output()
        elif sender is self.Mtl_Btns[1]:
            print Mtl_Btns[1] -left\nValue: %s % self.output()
        elif sender is self.Mtl_Btns[2]:
            print Mtl_Btns[2] -left\nValue: %s % self.output()
        elif sender is self.Mtl_Btns[3]:
            print Mtl_Btns[3] -left\nValue: %s % self.output()
        elif sender is self.Mtl_Btns[4]:
            print Mtl_Btns[4] -left\nValue: %s % self.output()
        elif sender is self.Mtl_Btns[5]:
            print Mtl_Btns[5] -left\nValue: %s % self.output()

以上がちょっとおしゃれなUIメソッドとなります
QSSのあたりやスライダーのあたりはかなり駆け足になってしまっているので後日詳しい詳細などを記事にできたらなとおもっています
これを機会におしゃれなUIを作成して見てください!


MayaPython Advent Calendar 2017の16日目の記事はsho7nokaさんのprint デバッグから卒業したい人のための Maya+PyCharmです