Как создать тему для PortProtonQt

Как создать тему для PortProtonQt

Это руководство поможет вам создать собственную тему оформления для PortProtonQt. Вы узнаете, как настроить цвета, шрифты, анимации и другие визуальные элементы.


Предупреждение о безопасности

Важно: Сторонние темы могут представлять риски:

  • Проверка безопасности — все темы проходят автоматическую проверку, которая блокирует опасные операции (доступ к файловой системе, сети, выполнение кода и т.д.). Однако это не 100% гарантия безопасности.
  • Не поддерживаются разработчиками — использование сторонних тем на ваш страх и риск. Разработчики PortProtonQt не несут ответственности за проблемы, вызванные сторонними темами.
  • Возможные уязвимости — темы содержат Python-код. Злоумышленник может найти способ обойти проверки.
  • Отсутствие гарантий — тема может содержать ошибки, устареть или нарушать работу приложения.
  • Проверяйте код — перед установкой внимательно изучайте styles.py и другие файлы темы.
  • Доверяйте источникам — загружайте темы только от проверенных авторов.

Рекомендация: Используйте встроенные темы или создавайте свои собственные.


Что нужно для создания темы

Для создания темы вам понадобятся:

  1. Папка с темой — место, где будут жить все файлы
  2. styles.py — файл со стилями (QSS) и константами
  3. metainfo.ini — название, автор, описание
  4. Скриншоты — чтобы показать, как тема выглядит
  5. Шрифты и иконки — опционально, если хотите кастомизацию

Всё это упаковывается в папку и кладётся в:

~/.local/share/PortProtonQT/themes/

Важно: Все константы (цвета, размеры, шрифты) и иконки нужно брать из встроенной темы standart. Смотрите её структуру в portprotonqt/themes/standart/ — это основной источник для копирования и адаптации под свою тему.


Шаг 1: Создаём папку темы

Придумайте название для темы (например, cyberpunk, minimal_dark, neon_vibe) и создайте папку:

mkdir -p ~/.local/share/PortProtonQT/themes/твоя_тема

Внутри этой папки будет вся структура темы.


Варианты темы (Light/Dark)

PortProtonQt поддерживает объединение светлой и тёмной версии темы в одну запись в списке тем.

Укажите связанные папки темы в metainfo.ini каждого варианта:

[Metainfo]
dark_variant = my_custom_theme
light_variant = my_custom_theme_light

Имена папок могут быть любыми допустимыми именами тем.

Если существуют обе указанные папки, приложение показывает отдельный выбор варианта: Dark, Light и Auto. Если существует только одна папка, выбор варианта скрывается, а доступная тема используется напрямую.

Auto используется как вариант по умолчанию. Он следует системной цветовой схеме

Оба варианта являются обычными темами и должны содержать собственный styles.py. Используйте THEME_INHERITS, если один вариант должен переиспользовать отсутствующие значения стилей из другой темы.

Важно: Если у темы есть оба варианта, добавьте dark_variant и light_variant в metainfo.ini обеих папок, чтобы любая выбранная папка могла связаться с той же парой вариантов.


Шаг 2: Пишем стили (styles.py)

Что такое QSS?

QSS (Qt Style Sheets) — это язык стилей для приложений на Qt. Он работает как CSS в веб-разработке: вы задаёте цвета, размеры, шрифты и другие визуальные свойства для кнопок, окон и других элементов.

Если вы знакомы с CSS — QSS покажется вам понятным. Синтаксис почти идентичен:

QPushButton { color: red; background: white }
QLineEdit { font-size: 16px }

В отличие от обычного CSS, QSS работает с виджетами (QPushButton, QLabel, QFrame и т.д.), а не с HTML-тегами.

Вариант А: Простой (всё в одном файле)

Если тема небольшая — создайте styles.py прямо в корне темы и пишите всё там:

# Константы
font_family = "Play"
font_size_a = "16px"
border_radius_a = "10px"
color_primary = "#409EFF"
color_background = "#282a33"

# Стили
MAIN_WINDOW_STYLE = f"""
    QMainWindow {{
        background: {color_background};
        font-family: {font_family};
        font-size: {font_size_a};
    }}
"""

GAME_CARD_STYLE = f"""
    QFrame {{
        background: {color_primary};
        border-radius: {border_radius_a};
    }}
"""

Вариант Б: Модульный (для больших тем)

Если тема разрастается — разбейте на подмодули. Создайте папку styles/ (или components/, как хотите) и внутри:

твоя_тема/
├── styles.py
├── metainfo.ini
├── fonts/
├── images/
└── styles/
    ├── __init__.py        # Пустой файл, делает папку пакетом
    ├── constants.py       # Все цвета, шрифты, размеры
    ├── base.py            # Базовые стили (окна, кнопки)
    ├── game_card.py       # Стили карточек игр
    ├── detail_page.py     # Страница деталей игры
    ├── settings.py        # Настройки
    ├── winetricks.py      # Winetricks диалоги
    └── theme_utils.py     # Вспомогательные стили

В главном styles.py импортируйте всё:

# Относительные импорты (рекомендуется для пользовательских тем)
from .styles.constants import *
from .styles.base import *
from .styles.game_card import *
from .styles.detail_page import *
from .styles.settings import *
from .styles.winetricks import *
from .styles.theme_utils import *

В каждом подмодуле пишите свои константы и стили:

# styles/constants.py
# Цвета
color_primary = "#409EFF"
color_background = "#282a33"
color_text = "#FFFFFF"
color_disabled_text = "#888888"

# Размеры
font_size_a = "16px"
font_size_b = "24px"
border_radius_a = "10px"
border_radius_b = "15px"

# Тени
shadow_blur_radius = 20
shadow_color = "#00000099"  # RGBA
# styles/base.py
from .constants import *

MAIN_WINDOW_STYLE = f"""
    QMainWindow {{
        background: {color_background};
        font-family: {font_family};
        font-size: {font_size_a};
        color: {color_text};
    }}
"""

BUTTON_STYLE = f"""
    QPushButton {{
        background: {color_primary};
        color: {color_text};
        border-radius: {border_radius_a};
        padding: 8px 16px;
    }}
    QPushButton:hover {{
        background: {color_primary_hover};
    }}
    QPushButton:disabled {{
        background: {color_disabled};
        color: {color_disabled_text};
    }}
"""

Важно: Имена переменных для стилей (например, MAIN_WINDOW_STYLE, BUTTON_STYLE, GAME_CARD_STYLE) нужно брать из встроенной темы standart (portprotonqt/themes/standart/styles/). Не придумывайте свои имена — приложение ожидает определённые названия переменных. Смотрите список в исходниках стандартной темы.


Наследование стилей

Тема может наследовать отсутствующие переменные и функции стилей от другой темы через THEME_INHERITS в styles.py:

THEME_INHERITS = "classic"

Если THEME_INHERITS не указан, тема наследует стили от standart.


Режим сетки библиотеки

Вы можете управлять типом отображения карточек библиотеки прямо из темы через styles.py:

# "grid" (по умолчанию) или "list"
LIBRARY_LAYOUT_MODE = "grid"
  • grid: многоколоночная сетка карточек (формат лаунчера).
  • list: горизонтальные карточки-строки (классическое поведение).

Это параметр уровня темы и не зависит от настроек приложения.


Режим отображения детальной страницы

Вы можете управлять отображением детальной страницы из темы через styles.py:

# "full" (по умолчанию) или "compact"
DETAIL_PAGE_LAYOUT_MODE = "full"
  • full: полный размер обложки, описание, бейджи, поддержка контроллера и данные HowLongToBeat.
  • compact: уменьшенная обложка и упрощённое содержимое детальной страницы.

Экономный режим также принудительно включает компактное отображение детальной страницы.


Цветовые схемы терминала

Цветовые схемы терминала задаются отдельными .conf файлами в Kitty-style формате. Они полностью совместимы с темами Kitty: можно взять любую готовую цветовую схему Kitty и положить её в пользовательскую папку схем PortProtonQt.

Встроенные схемы находятся в portprotonqt/terminal_schemes/. Пользовательские схемы можно положить в:

~/.local/share/PortProtonQt/terminal_schemes/

Имя файла без .conf используется как имя схемы. Меню терминала показывает все доступные .conf файлы из пользовательской и встроенной директорий.

Пример:

foreground #d4d4d4
background #1e1e1e
cursor #bbbbbb
selection_foreground #ffffff
selection_background #264f78
background_opacity 1.0
cursor_shape block
enable_audio_bell no

color0 #000000
color1 #cd3131
color2 #0dbc79
color3 #e5e510
color4 #2472c8
color5 #bc3fbc
color6 #11a8cd
color7 #e5e5e5

font_size 14
font_family Monospace

Поддерживаемые параметры терминала:

  • foreground, background: основные цвета текста и фона.
  • selection_foreground, selection_background: цвета выделенного текста.
  • cursor или cursor_color: цвет курсора.
  • cursor_shape: block, beam или underline.
  • enable_audio_bell: yes/no, true/false, on/off или 1/0.
  • background_opacity: прозрачность фона от 0.0 до 1.0.
  • font_size, font_family: настройки шрифта терминала.
  • color0 до color255: элементы ANSI-палитры.

Если cursor_shape не указан, терминал использует beam. Если enable_audio_bell не указан, звуковой сигнал выключен.


Шаг 3: Настраиваем анимации

В styles.py (или в отдельном файле, как удобно) определи словарь GAME_CARD_ANIMATION. Он управляет тем, как карточки игр анимируются при наведении, фокусе и переходе на детальную страницу.

GAME_CARD_ANIMATION = {
    # Тип анимации карточки: "gradient" (вращающийся градиент) или "scale" (увеличение)
    "card_animation_type": "gradient",
    
    # Толщина рамки в пикселях
    "default_border_width": 2,          # Обычное состояние
    "hover_border_width": 8,            # При наведении
    "focus_border_width": 12,           # При фокусе (клавиатура)
    "pulse_min_border_width": 8,        # Пульсация: минимум
    "pulse_max_border_width": 10,       # Пульсация: максимум
    
    # Длительность анимаций (в миллисекундах)
    "thickness_anim_duration": 300,     # Изменение толщины рамки
    "pulse_anim_duration": 800,         # Один цикл пульсации
    "gradient_anim_duration": 3000,     # Вращение градиента
    
    # Углы вращения градиента
    "gradient_start_angle": 360,
    "gradient_end_angle": 0,
    
    # Параметры для типов анимации fill, stripe и glow
    "fill_color": color_primary,
    "fill_alpha": 90,
    "stripe_color": color_primary,
    "stripe_alpha": 255,
    "glow_base_alpha": 120,
    "glow_pulse_alpha": 80,
    
    # Градиент для обводки (список цветов с позициями)
    "gradient_colors": [
        {"position": 0, "color": "#00fff5"},
        {"position": 0.33, "color": "#FF5733"},
        {"position": 0.66, "color": "#9B59B6"},
        {"position": 1, "color": "#00fff5"}
    ],
    
    # Масштаб карточки (1.0 = 100%)
    "default_scale": 1.0,
    "hover_scale": 1.1,      # 110% при наведении
    "focus_scale": 1.05,     # 105% при фокусе
    "scale_anim_duration": 200,
    
    # Плавность анимаций (названия из QEasingCurve.Type)
    "thickness_easing_curve": "OutBack",
    "thickness_easing_curve_out": "InBack",
    "scale_easing_curve": "OutBack",
    "scale_easing_curve_out": "InBack",
    
    # Анимация детальной страницы
    "detail_page_animation_type": "fade",  # "fade", "slide_left", "slide_right", "bounce"
    "detail_page_fade_duration": 350,
    "detail_page_slide_duration": 500,
    "detail_page_bounce_duration": 400,
    "detail_page_fade_duration_exit": 350,
    "detail_page_slide_duration_exit": 500,
    "detail_page_bounce_duration_exit": 400,
    "detail_page_easing_curve": "OutCubic",
    "detail_page_easing_curve_exit": "InCubic"
}

Типы анимаций

Параметр Значения Что делает
card_animation_type "gradient", "scale", "fill", "stripe", "glow", "scale_fill" Тип анимации карточки при наведении или фокусе
detail_page_animation_type "fade", "slide_left", "slide_right", "slide_up", "slide_down", "bounce" Как появляется детальная страница

Градиенты

Цвета градиента задаются списком словарей. Каждый цвет имеет позицию от 0.0 до 1.0:

"gradient_colors": [
    {"position": 0, "color": "#00fff5"},    # Начало (циан)
    {"position": 0.5, "color": "#FF5733"},  # Середина (оранжевый)
    {"position": 1, "color": "#00fff5"}     # Конец (снова циан)
]

Кривые плавности (Easing Curves)

Это то, как анимация «чувствуется» — плавно, с разгоном, с отскоком и т.д.

Популярные варианты:

  • "OutBack" — плавное замедление с небольшим «отскоком» в конце
  • "InOutQuad" — плавный разгон и замедление
  • "OutCubic" — быстрое начало, плавное замедление
  • "InBack" — начинается медленно, затем ускоряется

Попробуйте разные и посмотрите, что лучше подходит под стиль темы.

Анимации виртуальной клавиатуры

Настройки анимации виртуальной клавиатуры задаются константами темы:

virtual_keyboard_animation_type = "slide"  # "slide", "fade", "slide_fade", "slide_bounce"
virtual_keyboard_slide_animation_duration = 160
virtual_keyboard_fade_animation_duration = 140
virtual_keyboard_slide_fade_animation_duration = 180
virtual_keyboard_slide_bounce_animation_duration = 220

Шаг 4: Заполняем metainfo.ini

Этот файл рассказывает о теме пользователю. Создайте metainfo.ini в корне темы:

[Metainfo]
dark_variant = cyberpunk
light_variant = cyberpunk_light
name_en = Cyberpunk Neon
name_ru = Киберпанк Неон
author = ВашеИмя
author_link = https://example.com
description_en = Futuristic theme with neon colors and dark background.
description_ru = Футуристичная тема с неоновыми цветами и тёмным фоном.

Варианты темы

Если у темы есть светлый и тёмный варианты, укажите dark_variant и light_variant в metainfo.ini обеих папок темы. Если вариант только один — эти поля можно не добавлять.

Переводы

Обязательно укажите названия и описания на разных языках:

  • name_en, name_ru, name_fr и т.д.
  • description_en, description_ru, description_fr и т.д.

Приложение само выберет нужный язык в зависимости от системы пользователя. Если перевода нет — покажет английский.


Шаг 5: Добавляем скриншоты

Создайте папку images/screenshots/ внутри темы и положите туда скриншоты.

Важно: Скриншоты — это ваши собственные изображения. Не нужно копировать их из встроенной темы. Делайте скриншоты своей темы самостоятельно.

Называйте файлы как угодно, формат любой (PNG, JPG, WebP и т.д.). Скриншотьте всё, что считаете нужным для демонстрации темы — главное окно, отдельные элементы, анимации, настройки или любые другие детали, которые показывают особенности вашей темы.


Шаг 6: Шрифты и иконки (опционально)

Шрифты

Если хотите кастомный шрифт — положите файлы .ttf или .otf в папку fonts/:

твоя_тема/
└── fonts/
    ├── Play-Regular.ttf
    ├── Play-Bold.ttf
    └── Roboto-Mono.ttf

В constants.py укажите:

font_family = "Play"

Иконки

Важно: Берите названия иконок из встроенной темы standart (portprotonqt/themes/standart/images/icons/). Не придумывайте свои имена — используйте существующие файлы как референс.

Все иконки и изображения кладутся в images/. Система ищет иконки рекурсивно во всех подпапках, так что организовывайте как удобно:

твоя_тема/
└── images/
    ├── icons/
    │   ├── actions/
    │   │   ├── play.svg
    │   │   ├── pause.svg
    │   │   └── settings.svg
    │   ├── navigation/
    │   │   ├── arrow_left.svg
    │   │   └── arrow_right.svg
    │   ├── buttons/
    │   ├── keyboards/
    │   ├── controllers/
    │   │   ├── xbox/
    │   │   └── playstation/
    │   └── platforms/
    │       ├── steam.svg
    │       └── epic.svg
    ├── ui_elements/
    │   └── placeholder.png
    └── screenshots/
        └── main_window.png

Шаг 7: Тестируем тему

После того как всё готово:

  1. Перезапустите PortProtonQt — тема должна появиться в списке
  2. Проверьте все экраны — главное окно, детали, настройки, winetricks
  3. Пощёлкайте кнопки — убедитесь, что ховеры и фокусы работают
  4. Посмотрите на разных разрешениях — нет ли поломанной вёрстки

Удачи в создании темы!

Если что-то пойдёт не так — смотрите исходники встроенных тем в portprotonqt/themes/ для примеров.