mirror of
https://github.com/283375/arcaea-offline-pyside-ui.git
synced 2025-11-06 12:32:14 +00:00
wip: basic theming support
This commit is contained in:
49
app.py
49
app.py
@ -2,12 +2,13 @@ import sys
|
||||
from pathlib import Path
|
||||
|
||||
import structlog
|
||||
from PySide6.QtCore import QCoreApplication, QObject, Qt, QUrl
|
||||
from PySide6.QtCore import QCoreApplication, QEvent, QObject, Qt, QUrl
|
||||
from PySide6.QtGui import QGuiApplication, QIcon
|
||||
from PySide6.QtQml import QQmlApplicationEngine
|
||||
from PySide6.QtQuickControls2 import QQuickStyle
|
||||
|
||||
from ui.resources import resources_rc # noqa: F401
|
||||
from ui.theme import ThemeManager
|
||||
from ui.utils import url # noqa: F401
|
||||
from ui.viewmodels import overview # noqa: F401
|
||||
|
||||
@ -18,6 +19,34 @@ DEFAULT_FONTS = ["微软雅黑", "Microsoft YaHei UI", "Microsoft YaHei", "Segoe
|
||||
logger: structlog.stdlib.BoundLogger = structlog.get_logger()
|
||||
|
||||
|
||||
class ThemeChangeEventFilter(QObject):
|
||||
logger: structlog.stdlib.BoundLogger = structlog.get_logger(
|
||||
tag="ThemeChangeEventFilter",
|
||||
)
|
||||
|
||||
def __init__(self, *, themeManager: ThemeManager):
|
||||
super().__init__(None)
|
||||
self.themeManager = themeManager
|
||||
|
||||
self.scheme = self.themeManager.getCurrentScheme()
|
||||
|
||||
def doSomething(self) -> None:
|
||||
scheme = self.themeManager.getCurrentScheme()
|
||||
if scheme == self.scheme:
|
||||
self.logger.debug("Ignored same scheme event (%r)", scheme)
|
||||
return
|
||||
|
||||
self.scheme = scheme
|
||||
self.themeManager.updateTheme()
|
||||
self.logger.debug("something done")
|
||||
|
||||
def eventFilter(self, obj: QObject, event: QEvent) -> bool:
|
||||
if event.type() == QEvent.Type.ThemeChange:
|
||||
self.doSomething()
|
||||
|
||||
return False
|
||||
|
||||
|
||||
def main() -> None:
|
||||
app = QGuiApplication(sys.argv)
|
||||
app.setFont(DEFAULT_FONTS)
|
||||
@ -25,10 +54,20 @@ def main() -> None:
|
||||
app.setApplicationDisplayName("Arcaea Offline")
|
||||
app.setWindowIcon(QIcon(":/images/icon.png"))
|
||||
|
||||
QQuickStyle.setStyle("Fusion")
|
||||
|
||||
engine = QQmlApplicationEngine()
|
||||
|
||||
themeManager = ThemeManager(parent=app)
|
||||
|
||||
def onThemeManagerThemeChanged():
|
||||
logger.debug("App palette changed")
|
||||
app.setPalette(themeManager.qPalette) # pyright: ignore[reportArgumentType]
|
||||
engine.rootContext().setContextProperty("appTheme", themeManager.qmlExposer)
|
||||
|
||||
onThemeManagerThemeChanged()
|
||||
themeManager.themeChanged.connect(onThemeManagerThemeChanged)
|
||||
|
||||
QQuickStyle.setStyle("Fusion")
|
||||
|
||||
def onEngineObjectCreated(obj: QObject | None, objUrl: QUrl) -> None:
|
||||
if obj is None:
|
||||
logger.critical("rootObject is None! Exiting!")
|
||||
@ -41,6 +80,10 @@ def main() -> None:
|
||||
|
||||
engine.load("ui/qmls/App.qml")
|
||||
|
||||
rootObject = engine.rootObjects()[0]
|
||||
ef = ThemeChangeEventFilter(themeManager=themeManager)
|
||||
rootObject.installEventFilter(ef)
|
||||
|
||||
sys.exit(app.exec())
|
||||
|
||||
|
||||
|
||||
@ -16,6 +16,7 @@ dependencies = [
|
||||
"exif~=1.6.0",
|
||||
"PySide6==6.10.0",
|
||||
"Pillow~=10.1.0",
|
||||
"materialyoucolor~=2.0.10",
|
||||
]
|
||||
classifiers = [
|
||||
"Development Status :: 3 - Alpha",
|
||||
|
||||
@ -10,6 +10,45 @@ ApplicationWindow {
|
||||
width: 800
|
||||
height: 600
|
||||
|
||||
SystemPalette {
|
||||
id: systemPaletteActive
|
||||
colorGroup: SystemPalette.Active
|
||||
}
|
||||
|
||||
SystemPalette {
|
||||
id: systemPaletteDisabled
|
||||
colorGroup: SystemPalette.Disabled
|
||||
}
|
||||
|
||||
palette {
|
||||
accent: systemPaletteActive.accent
|
||||
alternateBase: systemPaletteActive.alternateBase
|
||||
base: systemPaletteActive.base
|
||||
button: systemPaletteActive.button
|
||||
buttonText: systemPaletteActive.buttonText
|
||||
dark: systemPaletteActive.dark
|
||||
highlight: systemPaletteActive.highlight
|
||||
highlightedText: systemPaletteActive.highlightedText
|
||||
light: systemPaletteActive.light
|
||||
mid: systemPaletteActive.mid
|
||||
midlight: systemPaletteActive.midlight
|
||||
placeholderText: systemPaletteActive.placeholderText
|
||||
shadow: systemPaletteActive.shadow
|
||||
text: systemPaletteActive.text
|
||||
window: systemPaletteActive.window
|
||||
windowText: systemPaletteActive.windowText
|
||||
|
||||
disabled {
|
||||
button: systemPaletteDisabled.button
|
||||
buttonText: systemPaletteDisabled.buttonText
|
||||
}
|
||||
|
||||
inactive {
|
||||
button: systemPaletteDisabled.button
|
||||
buttonText: systemPaletteDisabled.buttonText
|
||||
}
|
||||
}
|
||||
|
||||
StackLayout {
|
||||
id: stackLayout
|
||||
anchors.fill: parent
|
||||
|
||||
@ -27,8 +27,7 @@ ColumnLayout {
|
||||
function displayBool(value): string {
|
||||
if (value === undefined)
|
||||
return '-';
|
||||
// TODO: color success & error
|
||||
return value ? `<font color="lightgreen">Yes</font>` : `<font color="lightpink">No</font>`;
|
||||
return value ? `<font color="${appTheme.success}">Yes</font>` : `<font color="${appTheme.error}">No</font>`;
|
||||
}
|
||||
|
||||
component LabelLabel: Label {
|
||||
|
||||
@ -19,5 +19,8 @@
|
||||
|
||||
<file>lang/zh_CN.qm</file>
|
||||
<file>lang/en_US.qm</file>
|
||||
|
||||
<file>themes/default.json</file>
|
||||
<file>themes/tempest.json</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
|
||||
427
ui/resources/themes/default.json
Normal file
427
ui/resources/themes/default.json
Normal file
@ -0,0 +1,427 @@
|
||||
{
|
||||
"//url": "http://material-foundation.github.io/material-theme-builder/?primary=%234E486C&custom%3ASuccess=%2300C555&colorMatch=true",
|
||||
"name": "default",
|
||||
"description": "TYPE: CUSTOM\nMaterial Theme Builder export",
|
||||
"seed": "#4E486C",
|
||||
"coreColors": {
|
||||
"primary": "#4E486C"
|
||||
},
|
||||
"extendedColors": [
|
||||
{
|
||||
"name": "Success",
|
||||
"color": "#00C555",
|
||||
"description": "",
|
||||
"harmonized": true
|
||||
}
|
||||
],
|
||||
"schemes": {
|
||||
"light": {
|
||||
"primary": "#373154",
|
||||
"surfaceTint": "#605A7F",
|
||||
"onPrimary": "#FFFFFF",
|
||||
"primaryContainer": "#4E486C",
|
||||
"onPrimaryContainer": "#C0B8E3",
|
||||
"secondary": "#605C6C",
|
||||
"onSecondary": "#FFFFFF",
|
||||
"secondaryContainer": "#E3DDF0",
|
||||
"onSecondaryContainer": "#646071",
|
||||
"tertiary": "#4F2B40",
|
||||
"onTertiary": "#FFFFFF",
|
||||
"tertiaryContainer": "#684157",
|
||||
"onTertiaryContainer": "#E3B0CA",
|
||||
"error": "#BA1A1A",
|
||||
"onError": "#FFFFFF",
|
||||
"errorContainer": "#FFDAD6",
|
||||
"onErrorContainer": "#93000A",
|
||||
"background": "#FDF8FC",
|
||||
"onBackground": "#1C1B1E",
|
||||
"surface": "#FDF8FC",
|
||||
"onSurface": "#1C1B1E",
|
||||
"surfaceVariant": "#E6E1EA",
|
||||
"onSurfaceVariant": "#48464D",
|
||||
"outline": "#79767E",
|
||||
"outlineVariant": "#C9C5CE",
|
||||
"shadow": "#000000",
|
||||
"scrim": "#000000",
|
||||
"inverseSurface": "#313033",
|
||||
"inverseOnSurface": "#F4EFF3",
|
||||
"inversePrimary": "#C9C1EC",
|
||||
"primaryFixed": "#E6DEFF",
|
||||
"onPrimaryFixed": "#1C1738",
|
||||
"primaryFixedDim": "#C9C1EC",
|
||||
"onPrimaryFixedVariant": "#484266",
|
||||
"secondaryFixed": "#E6E0F3",
|
||||
"onSecondaryFixed": "#1C1A27",
|
||||
"secondaryFixedDim": "#C9C4D7",
|
||||
"onSecondaryFixedVariant": "#484554",
|
||||
"tertiaryFixed": "#FFD8EA",
|
||||
"onTertiaryFixed": "#301024",
|
||||
"tertiaryFixedDim": "#ECB8D2",
|
||||
"onTertiaryFixedVariant": "#613B51",
|
||||
"surfaceDim": "#DDD9DC",
|
||||
"surfaceBright": "#FDF8FC",
|
||||
"surfaceContainerLowest": "#FFFFFF",
|
||||
"surfaceContainerLow": "#F7F2F6",
|
||||
"surfaceContainer": "#F1ECF0",
|
||||
"surfaceContainerHigh": "#EBE7EA",
|
||||
"surfaceContainerHighest": "#E6E1E5"
|
||||
},
|
||||
"light-medium-contrast": {
|
||||
"primary": "#373154",
|
||||
"surfaceTint": "#605A7F",
|
||||
"onPrimary": "#FFFFFF",
|
||||
"primaryContainer": "#4E486C",
|
||||
"onPrimaryContainer": "#EDE7FF",
|
||||
"secondary": "#373443",
|
||||
"onSecondary": "#FFFFFF",
|
||||
"secondaryContainer": "#6F6B7B",
|
||||
"onSecondaryContainer": "#FFFFFF",
|
||||
"tertiary": "#4E2B40",
|
||||
"onTertiary": "#FFFFFF",
|
||||
"tertiaryContainer": "#684157",
|
||||
"onTertiaryContainer": "#FFE4EF",
|
||||
"error": "#740006",
|
||||
"onError": "#FFFFFF",
|
||||
"errorContainer": "#CF2C27",
|
||||
"onErrorContainer": "#FFFFFF",
|
||||
"background": "#FDF8FC",
|
||||
"onBackground": "#1C1B1E",
|
||||
"surface": "#FDF8FC",
|
||||
"onSurface": "#121113",
|
||||
"surfaceVariant": "#E6E1EA",
|
||||
"onSurfaceVariant": "#37353D",
|
||||
"outline": "#545159",
|
||||
"outlineVariant": "#6F6C74",
|
||||
"shadow": "#000000",
|
||||
"scrim": "#000000",
|
||||
"inverseSurface": "#313033",
|
||||
"inverseOnSurface": "#F4EFF3",
|
||||
"inversePrimary": "#C9C1EC",
|
||||
"primaryFixed": "#6F688E",
|
||||
"onPrimaryFixed": "#FFFFFF",
|
||||
"primaryFixedDim": "#565075",
|
||||
"onPrimaryFixedVariant": "#FFFFFF",
|
||||
"secondaryFixed": "#6F6B7B",
|
||||
"onSecondaryFixed": "#FFFFFF",
|
||||
"secondaryFixedDim": "#565363",
|
||||
"onSecondaryFixedVariant": "#FFFFFF",
|
||||
"tertiaryFixed": "#8B6078",
|
||||
"onTertiaryFixed": "#FFFFFF",
|
||||
"tertiaryFixedDim": "#71495F",
|
||||
"onTertiaryFixedVariant": "#FFFFFF",
|
||||
"surfaceDim": "#C9C5C9",
|
||||
"surfaceBright": "#FDF8FC",
|
||||
"surfaceContainerLowest": "#FFFFFF",
|
||||
"surfaceContainerLow": "#F7F2F6",
|
||||
"surfaceContainer": "#EBE7EA",
|
||||
"surfaceContainerHigh": "#E0DCDF",
|
||||
"surfaceContainerHighest": "#D4D0D4"
|
||||
},
|
||||
"light-high-contrast": {
|
||||
"primary": "#2D2749",
|
||||
"surfaceTint": "#605A7F",
|
||||
"onPrimary": "#FFFFFF",
|
||||
"primaryContainer": "#4A4468",
|
||||
"onPrimaryContainer": "#FFFFFF",
|
||||
"secondary": "#2D2A39",
|
||||
"onSecondary": "#FFFFFF",
|
||||
"secondaryContainer": "#4A4757",
|
||||
"onSecondaryContainer": "#FFFFFF",
|
||||
"tertiary": "#432135",
|
||||
"onTertiary": "#FFFFFF",
|
||||
"tertiaryContainer": "#643D53",
|
||||
"onTertiaryContainer": "#FFFFFF",
|
||||
"error": "#600004",
|
||||
"onError": "#FFFFFF",
|
||||
"errorContainer": "#98000A",
|
||||
"onErrorContainer": "#FFFFFF",
|
||||
"background": "#FDF8FC",
|
||||
"onBackground": "#1C1B1E",
|
||||
"surface": "#FDF8FC",
|
||||
"onSurface": "#000000",
|
||||
"surfaceVariant": "#E6E1EA",
|
||||
"onSurfaceVariant": "#000000",
|
||||
"outline": "#2D2B32",
|
||||
"outlineVariant": "#4A4850",
|
||||
"shadow": "#000000",
|
||||
"scrim": "#000000",
|
||||
"inverseSurface": "#313033",
|
||||
"inverseOnSurface": "#FFFFFF",
|
||||
"inversePrimary": "#C9C1EC",
|
||||
"primaryFixed": "#4A4468",
|
||||
"onPrimaryFixed": "#FFFFFF",
|
||||
"primaryFixedDim": "#342E50",
|
||||
"onPrimaryFixedVariant": "#FFFFFF",
|
||||
"secondaryFixed": "#4A4757",
|
||||
"onSecondaryFixed": "#FFFFFF",
|
||||
"secondaryFixedDim": "#34313F",
|
||||
"onSecondaryFixedVariant": "#FFFFFF",
|
||||
"tertiaryFixed": "#643D53",
|
||||
"onTertiaryFixed": "#FFFFFF",
|
||||
"tertiaryFixedDim": "#4A273C",
|
||||
"onTertiaryFixedVariant": "#FFFFFF",
|
||||
"surfaceDim": "#BBB8BB",
|
||||
"surfaceBright": "#FDF8FC",
|
||||
"surfaceContainerLowest": "#FFFFFF",
|
||||
"surfaceContainerLow": "#F4EFF3",
|
||||
"surfaceContainer": "#E6E1E5",
|
||||
"surfaceContainerHigh": "#D7D3D7",
|
||||
"surfaceContainerHighest": "#C9C5C9"
|
||||
},
|
||||
"dark": {
|
||||
"primary": "#C9C1EC",
|
||||
"surfaceTint": "#C9C1EC",
|
||||
"onPrimary": "#312C4E",
|
||||
"primaryContainer": "#4E486C",
|
||||
"onPrimaryContainer": "#C0B8E3",
|
||||
"secondary": "#C9C4D7",
|
||||
"onSecondary": "#312E3D",
|
||||
"secondaryContainer": "#4A4757",
|
||||
"onSecondaryContainer": "#BBB6C8",
|
||||
"tertiary": "#ECB8D2",
|
||||
"onTertiary": "#48253A",
|
||||
"tertiaryContainer": "#684157",
|
||||
"onTertiaryContainer": "#E3B0CA",
|
||||
"error": "#FFB4AB",
|
||||
"onError": "#690005",
|
||||
"errorContainer": "#93000A",
|
||||
"onErrorContainer": "#FFDAD6",
|
||||
"background": "#141315",
|
||||
"onBackground": "#E6E1E5",
|
||||
"surface": "#141315",
|
||||
"onSurface": "#E6E1E5",
|
||||
"surfaceVariant": "#48464D",
|
||||
"onSurfaceVariant": "#C9C5CE",
|
||||
"outline": "#938F98",
|
||||
"outlineVariant": "#48464D",
|
||||
"shadow": "#000000",
|
||||
"scrim": "#000000",
|
||||
"inverseSurface": "#E6E1E5",
|
||||
"inverseOnSurface": "#313033",
|
||||
"inversePrimary": "#605A7F",
|
||||
"primaryFixed": "#E6DEFF",
|
||||
"onPrimaryFixed": "#1C1738",
|
||||
"primaryFixedDim": "#C9C1EC",
|
||||
"onPrimaryFixedVariant": "#484266",
|
||||
"secondaryFixed": "#E6E0F3",
|
||||
"onSecondaryFixed": "#1C1A27",
|
||||
"secondaryFixedDim": "#C9C4D7",
|
||||
"onSecondaryFixedVariant": "#484554",
|
||||
"tertiaryFixed": "#FFD8EA",
|
||||
"onTertiaryFixed": "#301024",
|
||||
"tertiaryFixedDim": "#ECB8D2",
|
||||
"onTertiaryFixedVariant": "#613B51",
|
||||
"surfaceDim": "#141315",
|
||||
"surfaceBright": "#3A393B",
|
||||
"surfaceContainerLowest": "#0F0E10",
|
||||
"surfaceContainerLow": "#1C1B1E",
|
||||
"surfaceContainer": "#201F22",
|
||||
"surfaceContainerHigh": "#2B292C",
|
||||
"surfaceContainerHighest": "#363437"
|
||||
},
|
||||
"dark-medium-contrast": {
|
||||
"primary": "#E0D7FF",
|
||||
"surfaceTint": "#C9C1EC",
|
||||
"onPrimary": "#262142",
|
||||
"primaryContainer": "#938CB4",
|
||||
"onPrimaryContainer": "#000000",
|
||||
"secondary": "#DFD9ED",
|
||||
"onSecondary": "#262432",
|
||||
"secondaryContainer": "#938EA0",
|
||||
"onSecondaryContainer": "#000000",
|
||||
"tertiary": "#FFCFE7",
|
||||
"onTertiary": "#3C1A2E",
|
||||
"tertiaryContainer": "#B2839C",
|
||||
"onTertiaryContainer": "#000000",
|
||||
"error": "#FFD2CC",
|
||||
"onError": "#540003",
|
||||
"errorContainer": "#FF5449",
|
||||
"onErrorContainer": "#000000",
|
||||
"background": "#141315",
|
||||
"onBackground": "#E6E1E5",
|
||||
"surface": "#141315",
|
||||
"onSurface": "#FFFFFF",
|
||||
"surfaceVariant": "#48464D",
|
||||
"onSurfaceVariant": "#DFDAE4",
|
||||
"outline": "#B4B0BA",
|
||||
"outlineVariant": "#928F98",
|
||||
"shadow": "#000000",
|
||||
"scrim": "#000000",
|
||||
"inverseSurface": "#E6E1E5",
|
||||
"inverseOnSurface": "#2B292C",
|
||||
"inversePrimary": "#494367",
|
||||
"primaryFixed": "#E6DEFF",
|
||||
"onPrimaryFixed": "#120C2D",
|
||||
"primaryFixedDim": "#C9C1EC",
|
||||
"onPrimaryFixedVariant": "#373254",
|
||||
"secondaryFixed": "#E6E0F3",
|
||||
"onSecondaryFixed": "#120F1D",
|
||||
"secondaryFixedDim": "#C9C4D7",
|
||||
"onSecondaryFixedVariant": "#373443",
|
||||
"tertiaryFixed": "#FFD8EA",
|
||||
"onTertiaryFixed": "#230619",
|
||||
"tertiaryFixedDim": "#ECB8D2",
|
||||
"onTertiaryFixedVariant": "#4E2B40",
|
||||
"surfaceDim": "#141315",
|
||||
"surfaceBright": "#454447",
|
||||
"surfaceContainerLowest": "#080709",
|
||||
"surfaceContainerLow": "#1E1D20",
|
||||
"surfaceContainer": "#28272A",
|
||||
"surfaceContainerHigh": "#333235",
|
||||
"surfaceContainerHighest": "#3F3D40"
|
||||
},
|
||||
"dark-high-contrast": {
|
||||
"primary": "#F3EDFF",
|
||||
"surfaceTint": "#C9C1EC",
|
||||
"onPrimary": "#000000",
|
||||
"primaryContainer": "#C6BDE8",
|
||||
"onPrimaryContainer": "#0B0627",
|
||||
"secondary": "#F3EDFF",
|
||||
"onSecondary": "#000000",
|
||||
"secondaryContainer": "#C5C0D3",
|
||||
"onSecondaryContainer": "#0C0916",
|
||||
"tertiary": "#FFEBF3",
|
||||
"onTertiary": "#000000",
|
||||
"tertiaryContainer": "#E7B4CE",
|
||||
"onTertiaryContainer": "#1C0213",
|
||||
"error": "#FFECE9",
|
||||
"onError": "#000000",
|
||||
"errorContainer": "#FFAEA4",
|
||||
"onErrorContainer": "#220001",
|
||||
"background": "#141315",
|
||||
"onBackground": "#E6E1E5",
|
||||
"surface": "#141315",
|
||||
"onSurface": "#FFFFFF",
|
||||
"surfaceVariant": "#48464D",
|
||||
"onSurfaceVariant": "#FFFFFF",
|
||||
"outline": "#F3EEF8",
|
||||
"outlineVariant": "#C5C1CA",
|
||||
"shadow": "#000000",
|
||||
"scrim": "#000000",
|
||||
"inverseSurface": "#E6E1E5",
|
||||
"inverseOnSurface": "#000000",
|
||||
"inversePrimary": "#494367",
|
||||
"primaryFixed": "#E6DEFF",
|
||||
"onPrimaryFixed": "#000000",
|
||||
"primaryFixedDim": "#C9C1EC",
|
||||
"onPrimaryFixedVariant": "#120C2D",
|
||||
"secondaryFixed": "#E6E0F3",
|
||||
"onSecondaryFixed": "#000000",
|
||||
"secondaryFixedDim": "#C9C4D7",
|
||||
"onSecondaryFixedVariant": "#120F1D",
|
||||
"tertiaryFixed": "#FFD8EA",
|
||||
"onTertiaryFixed": "#000000",
|
||||
"tertiaryFixedDim": "#ECB8D2",
|
||||
"onTertiaryFixedVariant": "#230619",
|
||||
"surfaceDim": "#141315",
|
||||
"surfaceBright": "#514F52",
|
||||
"surfaceContainerLowest": "#000000",
|
||||
"surfaceContainerLow": "#201F22",
|
||||
"surfaceContainer": "#313033",
|
||||
"surfaceContainerHigh": "#3C3B3E",
|
||||
"surfaceContainerHighest": "#484649"
|
||||
}
|
||||
},
|
||||
"palettes": {
|
||||
"primary": {
|
||||
"0": "#000000",
|
||||
"5": "#110B2C",
|
||||
"10": "#1C1738",
|
||||
"15": "#272142",
|
||||
"20": "#312C4E",
|
||||
"25": "#3C375A",
|
||||
"30": "#484266",
|
||||
"35": "#544E72",
|
||||
"40": "#605A7F",
|
||||
"50": "#797299",
|
||||
"60": "#938CB4",
|
||||
"70": "#AEA6CF",
|
||||
"80": "#C9C1EC",
|
||||
"90": "#E6DEFF",
|
||||
"95": "#F4EEFF",
|
||||
"98": "#FDF8FF",
|
||||
"99": "#FFFBFF",
|
||||
"100": "#FFFFFF"
|
||||
},
|
||||
"secondary": {
|
||||
"0": "#000000",
|
||||
"5": "#111018",
|
||||
"10": "#1C1A23",
|
||||
"15": "#26252D",
|
||||
"20": "#312F38",
|
||||
"25": "#3C3A43",
|
||||
"30": "#48454F",
|
||||
"35": "#54515B",
|
||||
"40": "#605D67",
|
||||
"50": "#797580",
|
||||
"60": "#938F9A",
|
||||
"70": "#AEA9B5",
|
||||
"80": "#C9C4D0",
|
||||
"90": "#E6E0EC",
|
||||
"95": "#F4EEFB",
|
||||
"98": "#FDF8FF",
|
||||
"99": "#FFFBFF",
|
||||
"100": "#FFFFFF"
|
||||
},
|
||||
"tertiary": {
|
||||
"0": "#000000",
|
||||
"5": "#1B0C13",
|
||||
"10": "#27171E",
|
||||
"15": "#322128",
|
||||
"20": "#3E2B33",
|
||||
"25": "#49363E",
|
||||
"30": "#564149",
|
||||
"35": "#624D55",
|
||||
"40": "#6F5861",
|
||||
"50": "#897179",
|
||||
"60": "#A38A93",
|
||||
"70": "#BFA4AD",
|
||||
"80": "#DCBFC9",
|
||||
"90": "#F9DBE5",
|
||||
"95": "#FFECF1",
|
||||
"98": "#FFF8F8",
|
||||
"99": "#FFFBFF",
|
||||
"100": "#FFFFFF"
|
||||
},
|
||||
"neutral": {
|
||||
"0": "#000000",
|
||||
"5": "#111112",
|
||||
"10": "#1C1B1D",
|
||||
"15": "#262527",
|
||||
"20": "#313032",
|
||||
"25": "#3C3B3D",
|
||||
"30": "#484648",
|
||||
"35": "#545254",
|
||||
"40": "#605E60",
|
||||
"50": "#797678",
|
||||
"60": "#939092",
|
||||
"70": "#ADAAAC",
|
||||
"80": "#C9C5C7",
|
||||
"90": "#E5E1E3",
|
||||
"95": "#F4EFF1",
|
||||
"98": "#FDF8FA",
|
||||
"99": "#FFFBFF",
|
||||
"100": "#FFFFFF"
|
||||
},
|
||||
"neutral-variant": {
|
||||
"0": "#000000",
|
||||
"5": "#111014",
|
||||
"10": "#1C1B1F",
|
||||
"15": "#262529",
|
||||
"20": "#313034",
|
||||
"25": "#3C3B3F",
|
||||
"30": "#48464A",
|
||||
"35": "#545256",
|
||||
"40": "#605D62",
|
||||
"50": "#79767B",
|
||||
"60": "#939094",
|
||||
"70": "#AEAAAF",
|
||||
"80": "#C9C5CA",
|
||||
"90": "#E6E1E6",
|
||||
"95": "#F4EFF4",
|
||||
"98": "#FDF8FD",
|
||||
"99": "#FFFBFF",
|
||||
"100": "#FFFFFF"
|
||||
}
|
||||
}
|
||||
}
|
||||
427
ui/resources/themes/tempest.json
Normal file
427
ui/resources/themes/tempest.json
Normal file
@ -0,0 +1,427 @@
|
||||
{
|
||||
"//url": "http://material-foundation.github.io/material-theme-builder/?primary=%23186D98&custom%3ASuccess=%2300C555&colorMatch=true",
|
||||
"name": "tempest",
|
||||
"description": "TYPE: CUSTOM\nMaterial Theme Builder export",
|
||||
"seed": "#186D98",
|
||||
"coreColors": {
|
||||
"primary": "#186D98"
|
||||
},
|
||||
"extendedColors": [
|
||||
{
|
||||
"name": "Success",
|
||||
"color": "#00C555",
|
||||
"description": "",
|
||||
"harmonized": true
|
||||
}
|
||||
],
|
||||
"schemes": {
|
||||
"light": {
|
||||
"primary": "#005479",
|
||||
"surfaceTint": "#03658F",
|
||||
"onPrimary": "#FFFFFF",
|
||||
"primaryContainer": "#186D98",
|
||||
"onPrimaryContainer": "#CEE9FF",
|
||||
"secondary": "#496173",
|
||||
"onSecondary": "#FFFFFF",
|
||||
"secondaryContainer": "#C9E3F8",
|
||||
"onSecondaryContainer": "#4D6678",
|
||||
"tertiary": "#683D7A",
|
||||
"onTertiary": "#FFFFFF",
|
||||
"tertiaryContainer": "#825594",
|
||||
"onTertiaryContainer": "#F9DCFF",
|
||||
"error": "#BA1A1A",
|
||||
"onError": "#FFFFFF",
|
||||
"errorContainer": "#FFDAD6",
|
||||
"onErrorContainer": "#93000A",
|
||||
"background": "#F7F9FD",
|
||||
"onBackground": "#191C1F",
|
||||
"surface": "#F7F9FD",
|
||||
"onSurface": "#191C1F",
|
||||
"surfaceVariant": "#DCE3EB",
|
||||
"onSurfaceVariant": "#40484E",
|
||||
"outline": "#70787F",
|
||||
"outlineVariant": "#C0C7CF",
|
||||
"shadow": "#000000",
|
||||
"scrim": "#000000",
|
||||
"inverseSurface": "#2D3134",
|
||||
"inverseOnSurface": "#EFF1F5",
|
||||
"inversePrimary": "#88CEFE",
|
||||
"primaryFixed": "#C8E6FF",
|
||||
"onPrimaryFixed": "#001E2E",
|
||||
"primaryFixedDim": "#88CEFE",
|
||||
"onPrimaryFixedVariant": "#004C6D",
|
||||
"secondaryFixed": "#CCE6FB",
|
||||
"onSecondaryFixed": "#021E2D",
|
||||
"secondaryFixedDim": "#B0CADF",
|
||||
"onSecondaryFixedVariant": "#314A5B",
|
||||
"tertiaryFixed": "#F8D8FF",
|
||||
"onTertiaryFixed": "#300443",
|
||||
"tertiaryFixedDim": "#E8B4FA",
|
||||
"onTertiaryFixedVariant": "#603572",
|
||||
"surfaceDim": "#D8DADE",
|
||||
"surfaceBright": "#F7F9FD",
|
||||
"surfaceContainerLowest": "#FFFFFF",
|
||||
"surfaceContainerLow": "#F2F4F7",
|
||||
"surfaceContainer": "#ECEEF2",
|
||||
"surfaceContainerHigh": "#E6E8EC",
|
||||
"surfaceContainerHighest": "#E0E2E6"
|
||||
},
|
||||
"light-medium-contrast": {
|
||||
"primary": "#003A55",
|
||||
"surfaceTint": "#03658F",
|
||||
"onPrimary": "#FFFFFF",
|
||||
"primaryContainer": "#186D98",
|
||||
"onPrimaryContainer": "#FFFFFF",
|
||||
"secondary": "#203949",
|
||||
"onSecondary": "#FFFFFF",
|
||||
"secondaryContainer": "#577082",
|
||||
"onSecondaryContainer": "#FFFFFF",
|
||||
"tertiary": "#4E2460",
|
||||
"onTertiary": "#FFFFFF",
|
||||
"tertiaryContainer": "#825594",
|
||||
"onTertiaryContainer": "#FFFFFF",
|
||||
"error": "#740006",
|
||||
"onError": "#FFFFFF",
|
||||
"errorContainer": "#CF2C27",
|
||||
"onErrorContainer": "#FFFFFF",
|
||||
"background": "#F7F9FD",
|
||||
"onBackground": "#191C1F",
|
||||
"surface": "#F7F9FD",
|
||||
"onSurface": "#0E1214",
|
||||
"surfaceVariant": "#DCE3EB",
|
||||
"onSurfaceVariant": "#2F373D",
|
||||
"outline": "#4C535A",
|
||||
"outlineVariant": "#666E75",
|
||||
"shadow": "#000000",
|
||||
"scrim": "#000000",
|
||||
"inverseSurface": "#2D3134",
|
||||
"inverseOnSurface": "#EFF1F5",
|
||||
"inversePrimary": "#88CEFE",
|
||||
"primaryFixed": "#23749F",
|
||||
"onPrimaryFixed": "#FFFFFF",
|
||||
"primaryFixedDim": "#005B82",
|
||||
"onPrimaryFixedVariant": "#FFFFFF",
|
||||
"secondaryFixed": "#577082",
|
||||
"onSecondaryFixed": "#FFFFFF",
|
||||
"secondaryFixedDim": "#3F5869",
|
||||
"onSecondaryFixedVariant": "#FFFFFF",
|
||||
"tertiaryFixed": "#895C9B",
|
||||
"onTertiaryFixed": "#FFFFFF",
|
||||
"tertiaryFixedDim": "#6F4381",
|
||||
"onTertiaryFixedVariant": "#FFFFFF",
|
||||
"surfaceDim": "#C4C7CA",
|
||||
"surfaceBright": "#F7F9FD",
|
||||
"surfaceContainerLowest": "#FFFFFF",
|
||||
"surfaceContainerLow": "#F2F4F7",
|
||||
"surfaceContainer": "#E6E8EC",
|
||||
"surfaceContainerHigh": "#DBDDE1",
|
||||
"surfaceContainerHighest": "#CFD2D5"
|
||||
},
|
||||
"light-high-contrast": {
|
||||
"primary": "#003046",
|
||||
"surfaceTint": "#03658F",
|
||||
"onPrimary": "#FFFFFF",
|
||||
"primaryContainer": "#004E71",
|
||||
"onPrimaryContainer": "#FFFFFF",
|
||||
"secondary": "#152F3F",
|
||||
"onSecondary": "#FFFFFF",
|
||||
"secondaryContainer": "#344C5D",
|
||||
"onSecondaryContainer": "#FFFFFF",
|
||||
"tertiary": "#421955",
|
||||
"onTertiary": "#FFFFFF",
|
||||
"tertiaryContainer": "#623874",
|
||||
"onTertiaryContainer": "#FFFFFF",
|
||||
"error": "#600004",
|
||||
"onError": "#FFFFFF",
|
||||
"errorContainer": "#98000A",
|
||||
"onErrorContainer": "#FFFFFF",
|
||||
"background": "#F7F9FD",
|
||||
"onBackground": "#191C1F",
|
||||
"surface": "#F7F9FD",
|
||||
"onSurface": "#000000",
|
||||
"surfaceVariant": "#DCE3EB",
|
||||
"onSurfaceVariant": "#000000",
|
||||
"outline": "#252D33",
|
||||
"outlineVariant": "#424A51",
|
||||
"shadow": "#000000",
|
||||
"scrim": "#000000",
|
||||
"inverseSurface": "#2D3134",
|
||||
"inverseOnSurface": "#FFFFFF",
|
||||
"inversePrimary": "#88CEFE",
|
||||
"primaryFixed": "#004E71",
|
||||
"onPrimaryFixed": "#FFFFFF",
|
||||
"primaryFixedDim": "#003650",
|
||||
"onPrimaryFixedVariant": "#FFFFFF",
|
||||
"secondaryFixed": "#344C5D",
|
||||
"onSecondaryFixed": "#FFFFFF",
|
||||
"secondaryFixedDim": "#1C3546",
|
||||
"onSecondaryFixedVariant": "#FFFFFF",
|
||||
"tertiaryFixed": "#623874",
|
||||
"onTertiaryFixed": "#FFFFFF",
|
||||
"tertiaryFixedDim": "#4A205C",
|
||||
"onTertiaryFixedVariant": "#FFFFFF",
|
||||
"surfaceDim": "#B6B9BD",
|
||||
"surfaceBright": "#F7F9FD",
|
||||
"surfaceContainerLowest": "#FFFFFF",
|
||||
"surfaceContainerLow": "#EFF1F5",
|
||||
"surfaceContainer": "#E0E2E6",
|
||||
"surfaceContainerHigh": "#D2D4D8",
|
||||
"surfaceContainerHighest": "#C4C7CA"
|
||||
},
|
||||
"dark": {
|
||||
"primary": "#88CEFE",
|
||||
"surfaceTint": "#88CEFE",
|
||||
"onPrimary": "#00344D",
|
||||
"primaryContainer": "#186D98",
|
||||
"onPrimaryContainer": "#CEE9FF",
|
||||
"secondary": "#B0CADF",
|
||||
"onSecondary": "#1A3343",
|
||||
"secondaryContainer": "#314A5B",
|
||||
"onSecondaryContainer": "#9FB8CD",
|
||||
"tertiary": "#E8B4FA",
|
||||
"onTertiary": "#471E59",
|
||||
"tertiaryContainer": "#825594",
|
||||
"onTertiaryContainer": "#F9DCFF",
|
||||
"error": "#FFB4AB",
|
||||
"onError": "#690005",
|
||||
"errorContainer": "#93000A",
|
||||
"onErrorContainer": "#FFDAD6",
|
||||
"background": "#101417",
|
||||
"onBackground": "#E0E2E6",
|
||||
"surface": "#101417",
|
||||
"onSurface": "#E0E2E6",
|
||||
"surfaceVariant": "#40484E",
|
||||
"onSurfaceVariant": "#C0C7CF",
|
||||
"outline": "#8A9299",
|
||||
"outlineVariant": "#40484E",
|
||||
"shadow": "#000000",
|
||||
"scrim": "#000000",
|
||||
"inverseSurface": "#E0E2E6",
|
||||
"inverseOnSurface": "#2D3134",
|
||||
"inversePrimary": "#03658F",
|
||||
"primaryFixed": "#C8E6FF",
|
||||
"onPrimaryFixed": "#001E2E",
|
||||
"primaryFixedDim": "#88CEFE",
|
||||
"onPrimaryFixedVariant": "#004C6D",
|
||||
"secondaryFixed": "#CCE6FB",
|
||||
"onSecondaryFixed": "#021E2D",
|
||||
"secondaryFixedDim": "#B0CADF",
|
||||
"onSecondaryFixedVariant": "#314A5B",
|
||||
"tertiaryFixed": "#F8D8FF",
|
||||
"onTertiaryFixed": "#300443",
|
||||
"tertiaryFixedDim": "#E8B4FA",
|
||||
"onTertiaryFixedVariant": "#603572",
|
||||
"surfaceDim": "#101417",
|
||||
"surfaceBright": "#363A3D",
|
||||
"surfaceContainerLowest": "#0B0F11",
|
||||
"surfaceContainerLow": "#191C1F",
|
||||
"surfaceContainer": "#1D2023",
|
||||
"surfaceContainerHigh": "#272A2D",
|
||||
"surfaceContainerHighest": "#323538"
|
||||
},
|
||||
"dark-medium-contrast": {
|
||||
"primary": "#BBE1FF",
|
||||
"surfaceTint": "#88CEFE",
|
||||
"onPrimary": "#00293D",
|
||||
"primaryContainer": "#5098C5",
|
||||
"onPrimaryContainer": "#000000",
|
||||
"secondary": "#C6E0F5",
|
||||
"onSecondary": "#0D2838",
|
||||
"secondaryContainer": "#7B94A7",
|
||||
"onSecondaryContainer": "#000000",
|
||||
"tertiary": "#F5D0FF",
|
||||
"onTertiary": "#3B114E",
|
||||
"tertiaryContainer": "#AF7FC1",
|
||||
"onTertiaryContainer": "#000000",
|
||||
"error": "#FFD2CC",
|
||||
"onError": "#540003",
|
||||
"errorContainer": "#FF5449",
|
||||
"onErrorContainer": "#000000",
|
||||
"background": "#101417",
|
||||
"onBackground": "#E0E2E6",
|
||||
"surface": "#101417",
|
||||
"onSurface": "#FFFFFF",
|
||||
"surfaceVariant": "#40484E",
|
||||
"onSurfaceVariant": "#D6DDE5",
|
||||
"outline": "#ABB3BB",
|
||||
"outlineVariant": "#899199",
|
||||
"shadow": "#000000",
|
||||
"scrim": "#000000",
|
||||
"inverseSurface": "#E0E2E6",
|
||||
"inverseOnSurface": "#272A2D",
|
||||
"inversePrimary": "#004D6F",
|
||||
"primaryFixed": "#C8E6FF",
|
||||
"onPrimaryFixed": "#00131F",
|
||||
"primaryFixedDim": "#88CEFE",
|
||||
"onPrimaryFixedVariant": "#003A55",
|
||||
"secondaryFixed": "#CCE6FB",
|
||||
"onSecondaryFixed": "#00131F",
|
||||
"secondaryFixedDim": "#B0CADF",
|
||||
"onSecondaryFixedVariant": "#203949",
|
||||
"tertiaryFixed": "#F8D8FF",
|
||||
"onTertiaryFixed": "#220032",
|
||||
"tertiaryFixedDim": "#E8B4FA",
|
||||
"onTertiaryFixedVariant": "#4E2460",
|
||||
"surfaceDim": "#101417",
|
||||
"surfaceBright": "#424548",
|
||||
"surfaceContainerLowest": "#05080A",
|
||||
"surfaceContainerLow": "#1B1E21",
|
||||
"surfaceContainer": "#25282B",
|
||||
"surfaceContainerHigh": "#303336",
|
||||
"surfaceContainerHighest": "#3B3E41"
|
||||
},
|
||||
"dark-high-contrast": {
|
||||
"primary": "#E3F2FF",
|
||||
"surfaceTint": "#88CEFE",
|
||||
"onPrimary": "#000000",
|
||||
"primaryContainer": "#84CAFA",
|
||||
"onPrimaryContainer": "#000D17",
|
||||
"secondary": "#E3F2FF",
|
||||
"onSecondary": "#000000",
|
||||
"secondaryContainer": "#ACC6DB",
|
||||
"onSecondaryContainer": "#000D17",
|
||||
"tertiary": "#FDEAFF",
|
||||
"onTertiary": "#000000",
|
||||
"tertiaryContainer": "#E4B0F6",
|
||||
"onTertiaryContainer": "#190026",
|
||||
"error": "#FFECE9",
|
||||
"onError": "#000000",
|
||||
"errorContainer": "#FFAEA4",
|
||||
"onErrorContainer": "#220001",
|
||||
"background": "#101417",
|
||||
"onBackground": "#E0E2E6",
|
||||
"surface": "#101417",
|
||||
"onSurface": "#FFFFFF",
|
||||
"surfaceVariant": "#40484E",
|
||||
"onSurfaceVariant": "#FFFFFF",
|
||||
"outline": "#E9F1F9",
|
||||
"outlineVariant": "#BCC3CB",
|
||||
"shadow": "#000000",
|
||||
"scrim": "#000000",
|
||||
"inverseSurface": "#E0E2E6",
|
||||
"inverseOnSurface": "#000000",
|
||||
"inversePrimary": "#004D6F",
|
||||
"primaryFixed": "#C8E6FF",
|
||||
"onPrimaryFixed": "#000000",
|
||||
"primaryFixedDim": "#88CEFE",
|
||||
"onPrimaryFixedVariant": "#00131F",
|
||||
"secondaryFixed": "#CCE6FB",
|
||||
"onSecondaryFixed": "#000000",
|
||||
"secondaryFixedDim": "#B0CADF",
|
||||
"onSecondaryFixedVariant": "#00131F",
|
||||
"tertiaryFixed": "#F8D8FF",
|
||||
"onTertiaryFixed": "#000000",
|
||||
"tertiaryFixedDim": "#E8B4FA",
|
||||
"onTertiaryFixedVariant": "#220032",
|
||||
"surfaceDim": "#101417",
|
||||
"surfaceBright": "#4D5054",
|
||||
"surfaceContainerLowest": "#000000",
|
||||
"surfaceContainerLow": "#1D2023",
|
||||
"surfaceContainer": "#2D3134",
|
||||
"surfaceContainerHigh": "#383C3F",
|
||||
"surfaceContainerHighest": "#44474A"
|
||||
}
|
||||
},
|
||||
"palettes": {
|
||||
"primary": {
|
||||
"0": "#000000",
|
||||
"5": "#00131F",
|
||||
"10": "#001E2E",
|
||||
"15": "#00293D",
|
||||
"20": "#00344D",
|
||||
"25": "#00405D",
|
||||
"30": "#004C6D",
|
||||
"35": "#00587E",
|
||||
"40": "#03658F",
|
||||
"50": "#317EAA",
|
||||
"60": "#5098C5",
|
||||
"70": "#6CB3E1",
|
||||
"80": "#88CEFE",
|
||||
"90": "#C8E6FF",
|
||||
"95": "#E5F2FF",
|
||||
"98": "#F6FAFF",
|
||||
"99": "#FBFCFF",
|
||||
"100": "#FFFFFF"
|
||||
},
|
||||
"secondary": {
|
||||
"0": "#000000",
|
||||
"5": "#05121B",
|
||||
"10": "#0F1D26",
|
||||
"15": "#1A2731",
|
||||
"20": "#25323C",
|
||||
"25": "#303D47",
|
||||
"30": "#3B4853",
|
||||
"35": "#46545F",
|
||||
"40": "#52606B",
|
||||
"50": "#6B7984",
|
||||
"60": "#84929E",
|
||||
"70": "#9FADB9",
|
||||
"80": "#BAC8D5",
|
||||
"90": "#D6E4F2",
|
||||
"95": "#E5F2FF",
|
||||
"98": "#F6FAFF",
|
||||
"99": "#FBFCFF",
|
||||
"100": "#FFFFFF"
|
||||
},
|
||||
"tertiary": {
|
||||
"0": "#000000",
|
||||
"5": "#140D25",
|
||||
"10": "#1E1730",
|
||||
"15": "#29223B",
|
||||
"20": "#342C46",
|
||||
"25": "#3F3752",
|
||||
"30": "#4B425E",
|
||||
"35": "#564E6A",
|
||||
"40": "#635A76",
|
||||
"50": "#7C7290",
|
||||
"60": "#968CAB",
|
||||
"70": "#B1A6C6",
|
||||
"80": "#CDC1E2",
|
||||
"90": "#E9DDFF",
|
||||
"95": "#F6EDFF",
|
||||
"98": "#FEF7FF",
|
||||
"99": "#FFFBFF",
|
||||
"100": "#FFFFFF"
|
||||
},
|
||||
"neutral": {
|
||||
"0": "#000000",
|
||||
"5": "#0F1113",
|
||||
"10": "#1A1C1E",
|
||||
"15": "#242628",
|
||||
"20": "#2F3132",
|
||||
"25": "#3A3B3D",
|
||||
"30": "#454749",
|
||||
"35": "#515254",
|
||||
"40": "#5D5E60",
|
||||
"50": "#767779",
|
||||
"60": "#909193",
|
||||
"70": "#AAABAD",
|
||||
"80": "#C6C6C8",
|
||||
"90": "#E2E2E4",
|
||||
"95": "#F1F0F3",
|
||||
"98": "#F9F9FB",
|
||||
"99": "#FCFCFE",
|
||||
"100": "#FFFFFF"
|
||||
},
|
||||
"neutral-variant": {
|
||||
"0": "#000000",
|
||||
"5": "#0C1215",
|
||||
"10": "#171C20",
|
||||
"15": "#21262B",
|
||||
"20": "#2C3135",
|
||||
"25": "#373C40",
|
||||
"30": "#42474C",
|
||||
"35": "#4E5358",
|
||||
"40": "#5A5F64",
|
||||
"50": "#73787D",
|
||||
"60": "#8C9196",
|
||||
"70": "#A7ACB1",
|
||||
"80": "#C2C7CC",
|
||||
"90": "#DEE3E8",
|
||||
"95": "#EDF1F7",
|
||||
"98": "#F6FAFF",
|
||||
"99": "#FBFCFF",
|
||||
"100": "#FFFFFF"
|
||||
}
|
||||
}
|
||||
}
|
||||
3
ui/theme/__init__.py
Normal file
3
ui/theme/__init__.py
Normal file
@ -0,0 +1,3 @@
|
||||
from .manager import ThemeManager
|
||||
|
||||
__all__ = ["ThemeManager"]
|
||||
160
ui/theme/manager.py
Normal file
160
ui/theme/manager.py
Normal file
@ -0,0 +1,160 @@
|
||||
import dataclasses
|
||||
import json
|
||||
from typing import overload
|
||||
|
||||
import structlog
|
||||
from PySide6.QtCore import Property, QObject, QResource, Qt, Signal
|
||||
from PySide6.QtGui import QColor, QGuiApplication, QPalette
|
||||
|
||||
from .material3 import Material3DynamicThemeImpl, Material3ThemeImpl
|
||||
from .qml import ThemeQmlExposer
|
||||
from .shared import ThemeImpl, ThemeInfo, _TCustomPalette, _TScheme
|
||||
|
||||
QML_IMPORT_NAME = "internal.ui.theme"
|
||||
QML_IMPORT_MAJOR_VERSION = 1
|
||||
QML_IMPORT_MINOR_VERSION = 0
|
||||
|
||||
_THEME_CACHES: dict[ThemeInfo, ThemeImpl] = {}
|
||||
|
||||
logger: structlog.stdlib.BoundLogger = structlog.get_logger()
|
||||
|
||||
|
||||
class ThemeManager(QObject):
|
||||
_void = Signal()
|
||||
themeChanged = Signal()
|
||||
|
||||
def __init__(self, parent=None):
|
||||
super().__init__(parent)
|
||||
|
||||
self._qPalette = QPalette()
|
||||
self._customPalette: _TCustomPalette = {
|
||||
"primary": QColor.fromString("#616161"),
|
||||
"success": QColor.fromString("#616161"),
|
||||
"error": QColor.fromString("#616161"),
|
||||
}
|
||||
|
||||
self._lastThemeInfo = ThemeInfo(
|
||||
series="material3",
|
||||
name="default",
|
||||
scheme=self.getCurrentScheme(),
|
||||
)
|
||||
self._qmlExposer = ThemeQmlExposer(themeImpl=ThemeImpl())
|
||||
|
||||
self._cacheMaterial3Theme(themeName="default", scheme="light")
|
||||
self._cacheMaterial3Theme(themeName="default", scheme="dark")
|
||||
self._cacheMaterial3Theme(themeName="tempest", scheme="light")
|
||||
self._cacheMaterial3Theme(themeName="tempest", scheme="dark")
|
||||
|
||||
self._cacheMaterial3DynamicTheme(themeName="default", scheme="light")
|
||||
self._cacheMaterial3DynamicTheme(themeName="default", scheme="dark")
|
||||
self._cacheMaterial3DynamicTheme(themeName="tempest", scheme="light")
|
||||
self._cacheMaterial3DynamicTheme(themeName="tempest", scheme="dark")
|
||||
|
||||
self.setTheme("material3-dynamic", "default")
|
||||
|
||||
def getCurrentScheme(self) -> _TScheme:
|
||||
qApp: QGuiApplication = QGuiApplication.instance() # pyright: ignore[reportAssignmentType]
|
||||
return (
|
||||
"dark"
|
||||
if qApp.styleHints().colorScheme() == Qt.ColorScheme.Dark
|
||||
else "light"
|
||||
)
|
||||
|
||||
def getMaterial3Theme(self, themeName: str, scheme: _TScheme) -> Material3ThemeImpl:
|
||||
themeDataResource = QResource(f":/themes/{themeName}.json")
|
||||
if not themeDataResource.isValid():
|
||||
raise ValueError(f"Material3 theme {themeName!r} not found")
|
||||
|
||||
themeData = json.loads(
|
||||
themeDataResource.uncompressedData().data().decode("utf-8")
|
||||
)
|
||||
|
||||
return Material3ThemeImpl(themeData=themeData, scheme=scheme)
|
||||
|
||||
def getMaterial3DynamicTheme(
|
||||
self, themeName: str, scheme: _TScheme
|
||||
) -> Material3DynamicThemeImpl:
|
||||
themeDataResource = QResource(f":/themes/{themeName}.json")
|
||||
if not themeDataResource.isValid():
|
||||
raise ValueError(f"Material3 theme {themeName!r} not found")
|
||||
|
||||
themeData = json.loads(
|
||||
themeDataResource.uncompressedData().data().decode("utf-8")
|
||||
)
|
||||
|
||||
return Material3DynamicThemeImpl(
|
||||
sourceColorHex=themeData["seed"],
|
||||
scheme=scheme,
|
||||
name=themeName,
|
||||
)
|
||||
|
||||
def _cacheTheme(self, *, themeImpl: ThemeImpl):
|
||||
_THEME_CACHES[themeImpl.info] = themeImpl
|
||||
logger.debug("Theme %r cached", themeImpl.info)
|
||||
|
||||
def _getCachedTheme(self, *, themeInfo: ThemeInfo):
|
||||
cachedTheme = _THEME_CACHES.get(themeInfo)
|
||||
if cachedTheme is None:
|
||||
raise KeyError(f"Theme {themeInfo!r} not cached")
|
||||
return cachedTheme
|
||||
|
||||
def _cacheMaterial3Theme(self, *, themeName: str, scheme: _TScheme):
|
||||
self._cacheTheme(
|
||||
themeImpl=self.getMaterial3Theme(themeName=themeName, scheme=scheme),
|
||||
)
|
||||
|
||||
def _cacheMaterial3DynamicTheme(self, *, themeName: str, scheme: _TScheme):
|
||||
self._cacheTheme(
|
||||
themeImpl=self.getMaterial3DynamicTheme(themeName=themeName, scheme=scheme)
|
||||
)
|
||||
|
||||
@overload
|
||||
def setTheme(self, *, themeInfo: ThemeInfo): ...
|
||||
|
||||
@overload
|
||||
def setTheme(
|
||||
self, themeSeries: str, themeName: str, scheme: _TScheme | None = None, /
|
||||
): ...
|
||||
|
||||
def setTheme(self, *args, **kwargs):
|
||||
if "themeInfo" in kwargs:
|
||||
themeInfo = kwargs["themeInfo"]
|
||||
elif 2 <= len(args) <= 3:
|
||||
themeSeries = args[0]
|
||||
themeName = args[1]
|
||||
schemeArg = args[2] if len(args) > 2 else None
|
||||
scheme = schemeArg or self.getCurrentScheme()
|
||||
|
||||
themeInfo = ThemeInfo(series=themeSeries, name=themeName, scheme=scheme)
|
||||
else:
|
||||
raise TypeError("Invalid setTheme() call")
|
||||
|
||||
logger.debug("Preparing to set theme %r", themeInfo)
|
||||
|
||||
cachedTheme = self._getCachedTheme(themeInfo=themeInfo)
|
||||
|
||||
self._qPalette = cachedTheme.qPalette
|
||||
self._customPalette = cachedTheme.customPalette
|
||||
self._lastThemeInfo = themeInfo
|
||||
self._qmlExposer.themeImpl = cachedTheme
|
||||
|
||||
self.themeChanged.emit()
|
||||
|
||||
def updateTheme(self, scheme: _TScheme | None = None):
|
||||
themeInfo = dataclasses.replace(self._lastThemeInfo) # make a copy
|
||||
scheme = scheme or self.getCurrentScheme()
|
||||
themeInfo.scheme = scheme
|
||||
|
||||
self.setTheme(themeInfo=themeInfo)
|
||||
|
||||
@Property(QPalette, notify=themeChanged)
|
||||
def qPalette(self) -> QPalette:
|
||||
return self._qPalette
|
||||
|
||||
@Property(dict, notify=themeChanged)
|
||||
def customPalette(self) -> _TCustomPalette:
|
||||
return self._customPalette
|
||||
|
||||
@Property(ThemeQmlExposer, notify=themeChanged)
|
||||
def qmlExposer(self) -> ThemeQmlExposer:
|
||||
return self._qmlExposer
|
||||
245
ui/theme/material3.py
Normal file
245
ui/theme/material3.py
Normal file
@ -0,0 +1,245 @@
|
||||
from typing import TypedDict
|
||||
|
||||
from materialyoucolor.blend import Blend
|
||||
from materialyoucolor.dynamiccolor.contrast_curve import ContrastCurve
|
||||
from materialyoucolor.dynamiccolor.dynamic_color import DynamicColor, FromPaletteOptions
|
||||
from materialyoucolor.dynamiccolor.material_dynamic_colors import MaterialDynamicColors
|
||||
from materialyoucolor.hct import Hct
|
||||
from materialyoucolor.palettes.tonal_palette import TonalPalette
|
||||
from materialyoucolor.scheme.scheme_tonal_spot import SchemeTonalSpot
|
||||
from PySide6.QtGui import QColor, QPalette
|
||||
|
||||
from .shared import ThemeImpl, ThemeInfo, _TCustomPalette, _TScheme
|
||||
|
||||
|
||||
class _M3ThemeDataExtendedColorItem(TypedDict):
|
||||
name: str
|
||||
color: str
|
||||
description: str
|
||||
harmonized: bool
|
||||
|
||||
|
||||
_M3ThemeDataSchemes = TypedDict(
|
||||
"_M3ThemeDataSchemes",
|
||||
{
|
||||
"light": dict[str, str],
|
||||
"light-medium-contrast": dict[str, str],
|
||||
"light-high-contrast": dict[str, str],
|
||||
"dark": dict[str, str],
|
||||
"dark-medium-contrast": dict[str, str],
|
||||
"dark-high-contrast": dict[str, str],
|
||||
},
|
||||
)
|
||||
|
||||
_M3ThemeDataPalettes = TypedDict(
|
||||
"_M3ThemeDataPalettes",
|
||||
{
|
||||
"primary": dict[str, str],
|
||||
"secondary": dict[str, str],
|
||||
"tertiary": dict[str, str],
|
||||
"neutral": dict[str, str],
|
||||
"neutral-variant": dict[str, str],
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
class _M3ThemeData(TypedDict):
|
||||
name: str
|
||||
description: str
|
||||
seed: str
|
||||
coreColors: dict[str, str]
|
||||
extendedColors: list[_M3ThemeDataExtendedColorItem]
|
||||
schemes: _M3ThemeDataSchemes
|
||||
palettes: _M3ThemeDataPalettes
|
||||
|
||||
|
||||
def _hexToHct(hexColor: str) -> Hct:
|
||||
pureHexPart = hexColor[1:] if hexColor.startswith("#") else hexColor
|
||||
return Hct.from_int(int(f"0xff{pureHexPart}", 16))
|
||||
|
||||
|
||||
def _hctToQColor(hct: Hct) -> QColor:
|
||||
return QColor.fromRgba(hct.to_int())
|
||||
|
||||
|
||||
class Material3ThemeImpl(ThemeImpl):
|
||||
COLOR_ROLE_MAPPING: dict[QPalette.ColorRole, str] = {
|
||||
QPalette.ColorRole.Window: "surface",
|
||||
QPalette.ColorRole.WindowText: "onSurface",
|
||||
QPalette.ColorRole.Base: "surfaceContainer",
|
||||
QPalette.ColorRole.AlternateBase: "surfaceContainerHighest",
|
||||
QPalette.ColorRole.ToolTipBase: "secondaryContainer",
|
||||
QPalette.ColorRole.ToolTipText: "onSecondaryContainer",
|
||||
QPalette.ColorRole.PlaceholderText: "inverseSurface",
|
||||
QPalette.ColorRole.Text: "onSurface",
|
||||
QPalette.ColorRole.Button: "primaryContainer",
|
||||
QPalette.ColorRole.ButtonText: "onPrimaryContainer",
|
||||
QPalette.ColorRole.BrightText: "onSecondary",
|
||||
QPalette.ColorRole.Light: "surfaceContainerLowest",
|
||||
QPalette.ColorRole.Midlight: "surfaceContainerLow",
|
||||
QPalette.ColorRole.Dark: "inverseSurface",
|
||||
QPalette.ColorRole.Mid: "surfaceContainer",
|
||||
QPalette.ColorRole.Shadow: "shadow",
|
||||
QPalette.ColorRole.Highlight: "primary",
|
||||
QPalette.ColorRole.Accent: "primary",
|
||||
QPalette.ColorRole.HighlightedText: "onPrimary",
|
||||
QPalette.ColorRole.Link: "tertiary",
|
||||
QPalette.ColorRole.LinkVisited: "tertiaryContainer",
|
||||
}
|
||||
|
||||
def __init__(self, *, themeData: _M3ThemeData, scheme: _TScheme):
|
||||
self.themeData = themeData
|
||||
self.scheme: _TScheme = scheme
|
||||
|
||||
if self.themeData["schemes"].get(scheme) is None:
|
||||
raise ValueError(f"Invalid scheme: {scheme}")
|
||||
|
||||
def _findExtendedColor(
|
||||
self, colorName: str
|
||||
) -> _M3ThemeDataExtendedColorItem | None:
|
||||
return next(
|
||||
(it for it in self.themeData["extendedColors"] if it["name"] == colorName),
|
||||
None,
|
||||
)
|
||||
|
||||
@property
|
||||
def info(self):
|
||||
return ThemeInfo(
|
||||
series="material3",
|
||||
name=self.themeData["name"],
|
||||
scheme=self.scheme,
|
||||
)
|
||||
|
||||
@property
|
||||
def qPalette(self) -> QPalette:
|
||||
qPalette = QPalette()
|
||||
|
||||
for role, name in self.COLOR_ROLE_MAPPING.items():
|
||||
color = QColor.fromString(self.themeData["schemes"][self.scheme][name])
|
||||
qPalette.setColor(role, color)
|
||||
|
||||
return qPalette
|
||||
|
||||
@property
|
||||
def customPalette(self) -> _TCustomPalette:
|
||||
primaryHct = _hexToHct(self.themeData["schemes"][self.scheme]["primary"])
|
||||
|
||||
successColorItem = self._findExtendedColor("Success")
|
||||
if successColorItem is None:
|
||||
raise Exception("Success color not found")
|
||||
successHct = _hexToHct(successColorItem["color"])
|
||||
|
||||
successHarmonizedHct = Hct.from_int(
|
||||
Blend.harmonize(successHct.to_int(), primaryHct.to_int())
|
||||
)
|
||||
|
||||
return {
|
||||
"primary": _hctToQColor(primaryHct),
|
||||
"success": _hctToQColor(successHarmonizedHct),
|
||||
"error": QColor.fromString(self.themeData["schemes"][self.scheme]["error"]),
|
||||
}
|
||||
|
||||
|
||||
class Material3DynamicThemeImpl(ThemeImpl):
|
||||
ACTIVE_COLOR_ROLE_MAPPING: dict[QPalette.ColorRole, DynamicColor] = {
|
||||
QPalette.ColorRole.Window: MaterialDynamicColors.surface,
|
||||
QPalette.ColorRole.WindowText: MaterialDynamicColors.onSurface,
|
||||
QPalette.ColorRole.Base: MaterialDynamicColors.surfaceContainer,
|
||||
QPalette.ColorRole.AlternateBase: MaterialDynamicColors.surfaceContainerHighest,
|
||||
QPalette.ColorRole.ToolTipBase: MaterialDynamicColors.secondaryContainer,
|
||||
QPalette.ColorRole.ToolTipText: MaterialDynamicColors.onSecondaryContainer,
|
||||
QPalette.ColorRole.PlaceholderText: MaterialDynamicColors.inverseSurface,
|
||||
QPalette.ColorRole.Text: MaterialDynamicColors.onSurface,
|
||||
QPalette.ColorRole.Button: MaterialDynamicColors.primaryContainer,
|
||||
QPalette.ColorRole.ButtonText: MaterialDynamicColors.onPrimaryContainer,
|
||||
QPalette.ColorRole.BrightText: MaterialDynamicColors.onSecondary,
|
||||
QPalette.ColorRole.Light: MaterialDynamicColors.surfaceContainerLowest,
|
||||
QPalette.ColorRole.Midlight: MaterialDynamicColors.surfaceContainerLow,
|
||||
QPalette.ColorRole.Dark: MaterialDynamicColors.inverseSurface,
|
||||
QPalette.ColorRole.Mid: MaterialDynamicColors.surfaceContainer,
|
||||
QPalette.ColorRole.Shadow: MaterialDynamicColors.shadow,
|
||||
QPalette.ColorRole.Highlight: MaterialDynamicColors.primary,
|
||||
QPalette.ColorRole.Accent: MaterialDynamicColors.primary,
|
||||
QPalette.ColorRole.HighlightedText: MaterialDynamicColors.onPrimary,
|
||||
QPalette.ColorRole.Link: MaterialDynamicColors.tertiary,
|
||||
QPalette.ColorRole.LinkVisited: MaterialDynamicColors.tertiaryContainer,
|
||||
}
|
||||
|
||||
EXTENDED_COLORS = {
|
||||
"success": "#00c555",
|
||||
}
|
||||
|
||||
def __init__(self, sourceColorHex: str, scheme: _TScheme, *, name: str):
|
||||
self.material3Scheme = SchemeTonalSpot(
|
||||
_hexToHct(sourceColorHex),
|
||||
is_dark=scheme == "dark",
|
||||
contrast_level=0.0,
|
||||
)
|
||||
self.name = name
|
||||
|
||||
@property
|
||||
def info(self):
|
||||
return ThemeInfo(
|
||||
series="material3-dynamic",
|
||||
name=self.name,
|
||||
scheme="dark" if self.material3Scheme.is_dark else "light",
|
||||
)
|
||||
|
||||
@property
|
||||
def qPalette(self) -> QPalette:
|
||||
qPalette = QPalette()
|
||||
|
||||
for role, dynamicColor in self.ACTIVE_COLOR_ROLE_MAPPING.items():
|
||||
hct = dynamicColor.get_hct(self.material3Scheme)
|
||||
qColor = QColor.fromRgba(hct.to_int())
|
||||
qPalette.setColor(QPalette.ColorGroup.Active, role, qColor)
|
||||
|
||||
# TODO: disabled palette seems to work only after a theme reload, needs further investigation
|
||||
if role in [QPalette.ColorRole.Button, QPalette.ColorRole.ButtonText]:
|
||||
disabledHct = Hct.from_hct(hct.hue, 1.0, hct.tone)
|
||||
disabledQColor = QColor.fromRgba(disabledHct.to_int())
|
||||
qPalette.setColor(QPalette.ColorGroup.Disabled, role, disabledQColor)
|
||||
|
||||
return qPalette
|
||||
|
||||
@property
|
||||
def customPalette(self) -> _TCustomPalette:
|
||||
primaryHct = MaterialDynamicColors.primary.get_hct(self.material3Scheme)
|
||||
errorHct = MaterialDynamicColors.error.get_hct(self.material3Scheme)
|
||||
|
||||
extendedPalettes: dict[str, DynamicColor] = {}
|
||||
for colorName, colorHex in self.EXTENDED_COLORS.items():
|
||||
colorHct = _hexToHct(colorHex)
|
||||
colorHarmonized = Blend.harmonize(colorHct.to_int(), primaryHct.to_int())
|
||||
|
||||
colorTonalPalette = TonalPalette.from_int(colorHarmonized)
|
||||
|
||||
colorSurfacePaletteOptions = DynamicColor.from_palette(
|
||||
FromPaletteOptions(
|
||||
name=f"{colorName}_container",
|
||||
palette=lambda s: colorTonalPalette,
|
||||
tone=lambda s: 30 if s.is_dark else 90,
|
||||
is_background=True,
|
||||
background=lambda s: MaterialDynamicColors.highestSurface(s),
|
||||
contrast_curve=ContrastCurve(1, 1, 3, 4.5),
|
||||
)
|
||||
)
|
||||
|
||||
extendedPalettes[colorName] = DynamicColor.from_palette(
|
||||
FromPaletteOptions(
|
||||
name=colorName, # pyright: ignore[reportArgumentType]
|
||||
palette=lambda s: colorTonalPalette,
|
||||
tone=lambda s: 80 if s.is_dark else 40, # pyright: ignore[reportArgumentType]
|
||||
is_background=False, # pyright: ignore[reportArgumentType]
|
||||
background=lambda s: MaterialDynamicColors.highestSurface(s),
|
||||
contrast_curve=ContrastCurve(3, 4.5, 7, 7),
|
||||
)
|
||||
)
|
||||
|
||||
return {
|
||||
"primary": _hctToQColor(primaryHct),
|
||||
"success": _hctToQColor(
|
||||
extendedPalettes["success"].get_hct(self.material3Scheme)
|
||||
),
|
||||
"error": _hctToQColor(errorHct),
|
||||
}
|
||||
37
ui/theme/qml.py
Normal file
37
ui/theme/qml.py
Normal file
@ -0,0 +1,37 @@
|
||||
from PySide6.QtCore import Property, QObject, Signal
|
||||
from PySide6.QtGui import QColor
|
||||
|
||||
from .shared import ThemeImpl
|
||||
|
||||
QML_IMPORT_NAME = "internal.ui.theme"
|
||||
QML_IMPORT_MAJOR_VERSION = 1
|
||||
QML_IMPORT_MINOR_VERSION = 0
|
||||
|
||||
|
||||
class ThemeQmlExposer(QObject):
|
||||
themeChanged = Signal()
|
||||
|
||||
def __init__(self, *, themeImpl: ThemeImpl, parent: QObject | None = None):
|
||||
super().__init__(parent)
|
||||
self._themeImpl = themeImpl
|
||||
|
||||
@property
|
||||
def themeImpl(self) -> ThemeImpl:
|
||||
return self._themeImpl
|
||||
|
||||
@themeImpl.setter
|
||||
def themeImpl(self, themeImpl: ThemeImpl):
|
||||
self._themeImpl = themeImpl
|
||||
self.themeChanged.emit()
|
||||
|
||||
@Property(QColor, notify=themeChanged)
|
||||
def primary(self):
|
||||
return self._themeImpl.customPalette["primary"]
|
||||
|
||||
@Property(QColor, notify=themeChanged)
|
||||
def success(self):
|
||||
return self._themeImpl.customPalette["success"]
|
||||
|
||||
@Property(QColor, notify=themeChanged)
|
||||
def error(self):
|
||||
return self._themeImpl.customPalette["error"]
|
||||
43
ui/theme/shared.py
Normal file
43
ui/theme/shared.py
Normal file
@ -0,0 +1,43 @@
|
||||
from dataclasses import dataclass
|
||||
from typing import Literal, TypedDict
|
||||
|
||||
from PySide6.QtGui import QColor, QPalette
|
||||
|
||||
|
||||
class _TCustomPalette(TypedDict):
|
||||
primary: QColor
|
||||
success: QColor
|
||||
error: QColor
|
||||
|
||||
|
||||
_TScheme = Literal["light", "dark"]
|
||||
|
||||
|
||||
@dataclass
|
||||
class ThemeInfo:
|
||||
series: str
|
||||
name: str
|
||||
scheme: _TScheme
|
||||
|
||||
def __hash__(self) -> int:
|
||||
return hash((self.series, self.name, self.scheme))
|
||||
|
||||
|
||||
class ThemeImpl:
|
||||
DEFAULT_CUSTOM_PALETTE = {
|
||||
"primary": QColor.fromString("#616161"),
|
||||
"success": QColor.fromString("#616161"),
|
||||
"error": QColor.fromString("#616161"),
|
||||
}
|
||||
|
||||
@property
|
||||
def info(self) -> ThemeInfo:
|
||||
return ThemeInfo(series="placeholder", name="placeholder", scheme="dark")
|
||||
|
||||
@property
|
||||
def qPalette(self) -> QPalette:
|
||||
return QPalette()
|
||||
|
||||
@property
|
||||
def customPalette(self) -> _TCustomPalette:
|
||||
return self.DEFAULT_CUSTOM_PALETTE # pyright: ignore[reportReturnType]
|
||||
25
uv.lock
generated
25
uv.lock
generated
@ -51,6 +51,7 @@ dependencies = [
|
||||
{ name = "arcaea-offline-ocr" },
|
||||
{ name = "colorama" },
|
||||
{ name = "exif" },
|
||||
{ name = "materialyoucolor" },
|
||||
{ name = "pillow" },
|
||||
{ name = "pyside6" },
|
||||
{ name = "rich" },
|
||||
@ -72,6 +73,7 @@ requires-dist = [
|
||||
{ name = "colorama", specifier = "~=0.4.6" },
|
||||
{ name = "exif", specifier = "~=1.6.0" },
|
||||
{ name = "imageio", marker = "extra == 'dev'" },
|
||||
{ name = "materialyoucolor", specifier = "~=2.0.10" },
|
||||
{ name = "nuitka", marker = "extra == 'dev'", specifier = "~=2.7.6" },
|
||||
{ name = "pillow", specifier = "~=10.1.0" },
|
||||
{ name = "pre-commit", marker = "extra == 'dev'", specifier = ">=4.3.0" },
|
||||
@ -293,6 +295,29 @@ wheels = [
|
||||
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/94/54/e7d793b573f298e1c9013b8c4dade17d481164aa517d1d7148619c2cedbf/markdown_it_py-4.0.0-py3-none-any.whl", hash = "sha256:87327c59b172c5011896038353a81343b6754500a08cd7a4973bb48c6d578147", size = 87321, upload-time = "2025-08-11T12:57:51.923Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "materialyoucolor"
|
||||
version = "2.0.10"
|
||||
source = { registry = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/simple/" }
|
||||
sdist = { url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/83/b3/8835bacf50ea32bf000f9118f60da4c8e5febe80ab5929be18ce99a4baea/materialyoucolor-2.0.10.tar.gz", hash = "sha256:31b4d407b9a4fd4b54b30559b0d0313f6d6da1f9d19b75546ed65da52671ea76", size = 250103, upload-time = "2025-01-09T07:56:43.05Z" }
|
||||
wheels = [
|
||||
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/38/52/36cf8bbb3989f31328ca2a06819b17c021052bcd97a7e703eadace1f225f/materialyoucolor-2.0.10-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:4de08ee488369cf0e055ae17ac43c62684f618781939feaf0ad11bc32572a4b6", size = 316148, upload-time = "2025-01-09T09:02:52.364Z" },
|
||||
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/ea/96/4f3919588aa341f8152ad01de784924ac26059077b095069f2ca83525053/materialyoucolor-2.0.10-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ad22b0fb6980e0995d5b8437cad3411f628e1ea88c223c94a20b59d5dbcbacb2", size = 206497, upload-time = "2025-01-09T08:34:31.692Z" },
|
||||
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/5e/91/05303aa40da51a71c4e828781a0a34b5ba8c88a9a0f2eec245c1257fe82e/materialyoucolor-2.0.10-cp310-cp310-win_amd64.whl", hash = "sha256:997787ec4224f3e2d9c90e99d82db746f5581670564a22a0f182ad377e704761", size = 150420, upload-time = "2025-01-09T09:02:55.842Z" },
|
||||
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/04/eb/78d067df3948bd6a347677c10d35d89b6092916f78e0b98c51d47fd080f5/materialyoucolor-2.0.10-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:e9a4dd1e444a64ea54982be06314ce4611f3c38fdfae2c33cca7d106f6383288", size = 319432, upload-time = "2025-01-09T09:02:51.999Z" },
|
||||
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/29/b1/04424f2bd811fe7e803d8a21cc28e8b4f9dea8b07cbc948318d4b4de3dd8/materialyoucolor-2.0.10-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:144fdd9dca272fee60a1779ce35acf24830cd3bc33ff20982ecba4936cb5bf91", size = 208262, upload-time = "2025-01-09T08:34:34.198Z" },
|
||||
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/82/43/926ae82c04e5a5d02523f66e48af8021c0fdd8e01463a51e934137f7a5a0/materialyoucolor-2.0.10-cp311-cp311-win_amd64.whl", hash = "sha256:f126196a051673707297e86b8d0fbd90805a552dc3ac79e1d90442cc3fc877cb", size = 151620, upload-time = "2025-01-09T09:02:52.028Z" },
|
||||
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/69/e8/847974020cde0e09124d7fb51bf41aa92cc3a9cad83f83911e5d67c1d665/materialyoucolor-2.0.10-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:0fc0b43a684536270177b71bf509b247dab4ff816e49d1ac1b124fc1a32e082e", size = 318810, upload-time = "2025-01-09T09:02:52.905Z" },
|
||||
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/fa/67/2f3e70d707f86be3c541327693db5105622aa6aa1079d48ff90876d2e650/materialyoucolor-2.0.10-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7fe5bbc63124c3ea2f5560ea4a5250af72fce9ab55d29925e6bac7c7ec65329d", size = 208373, upload-time = "2025-01-09T08:34:35.656Z" },
|
||||
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/ef/f2/1119e97e741b33717e26c494b4aa3c5ca57d770df4fa42707b698bfb7588/materialyoucolor-2.0.10-cp312-cp312-win_amd64.whl", hash = "sha256:aedc8596fd7583fd9e23f3e6d4f7b9defd7d5ef6e7889967814b9900c1e3d5b3", size = 151716, upload-time = "2025-01-09T09:02:57.1Z" },
|
||||
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/ef/49/c4517231ec21bdd91a301cb40fdf86c632a95b10d86bc3278bd5d619480c/materialyoucolor-2.0.10-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:71de9c581c8227c3010c61108a00af9a007c96f99358fdb0628626d0ade817d9", size = 318890, upload-time = "2025-01-09T09:02:52.276Z" },
|
||||
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/4d/62/807bea1db749dc45debc2afa51ea4b27f5a219fbe73f6458c2bae2a15427/materialyoucolor-2.0.10-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:978748eca7725d8b569cdecfd9f86a8e34bf946182f79a48b059c98a10f1a0de", size = 208339, upload-time = "2025-01-09T08:34:38.213Z" },
|
||||
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/2a/01/05075b959b228c5c99c80703b7c63532ac6ee49abdf8054077e70908d6e3/materialyoucolor-2.0.10-cp313-cp313-win_amd64.whl", hash = "sha256:2ffc8963a2f10d8acb6776e60e97f0e7929d3392eb282cf2bc63b8e56cd930b3", size = 151751, upload-time = "2025-01-09T09:02:53.299Z" },
|
||||
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/01/56/29e59a5bcc92f0dc0a1de728e711edfcd971a6ef269a97cf461642fc339d/materialyoucolor-2.0.10-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:e4827307b1c78897f97f7989237ef688a5c21663a63d327d7cec8e47f107e509", size = 316289, upload-time = "2025-01-09T09:02:53.101Z" },
|
||||
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/3c/be/039f46e565b77ce088902c19352c073e16c34a4a8743c95fcad1a537d81f/materialyoucolor-2.0.10-cp39-cp39-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d8cebae884c6106584e1792b910d9588778353255a1a8156cb634e21f1b8670e", size = 206468, upload-time = "2025-01-09T07:56:41.508Z" },
|
||||
{ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/6b/b9/d8946d2d9901ca7bd088ea8efcdc975c73f51766b234fc3a83b821988b2b/materialyoucolor-2.0.10-cp39-cp39-win_amd64.whl", hash = "sha256:4c5141fdd973ab386ebd0242072a7faab28d19cc083ee0d14f5b70c31ad75d60", size = 150332, upload-time = "2025-01-09T09:02:56.525Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mdurl"
|
||||
version = "0.1.2"
|
||||
|
||||
Reference in New Issue
Block a user