Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
36db738601 | ||
|
|
18dfe41e39 | ||
|
|
df447a1ecf | ||
|
|
01395a5aa4 | ||
|
|
74dd9824f3 | ||
|
|
11d03556da | ||
|
|
02c7b9483b | ||
|
|
8e6e54c802 | ||
|
|
603f6827b6 | ||
|
|
41999b31c1 | ||
|
|
6e2defe3b6 | ||
|
|
ec5b85573c |
No files matched your search
+174
@@ -0,0 +1,174 @@
|
||||
# Byte-compiled / optimized / DLL files
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
*$py.class
|
||||
|
||||
# C extensions
|
||||
*.so
|
||||
|
||||
# Distribution / packaging
|
||||
.Python
|
||||
build/
|
||||
develop-eggs/
|
||||
dist/
|
||||
downloads/
|
||||
eggs/
|
||||
.eggs/
|
||||
lib/
|
||||
lib64/
|
||||
parts/
|
||||
sdist/
|
||||
var/
|
||||
wheels/
|
||||
share/python-wheels/
|
||||
*.egg-info/
|
||||
.installed.cfg
|
||||
*.egg
|
||||
MANIFEST
|
||||
|
||||
# PyInstaller
|
||||
# Usually these files are written by a python script from a template
|
||||
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
||||
*.manifest
|
||||
*.spec
|
||||
|
||||
# Installer logs
|
||||
pip-log.txt
|
||||
pip-delete-this-directory.txt
|
||||
|
||||
# Unit test / coverage reports
|
||||
htmlcov/
|
||||
.tox/
|
||||
.nox/
|
||||
.coverage
|
||||
.coverage.*
|
||||
.cache
|
||||
nosetests.xml
|
||||
coverage.xml
|
||||
*.cover
|
||||
*.py,cover
|
||||
.hypothesis/
|
||||
.pytest_cache/
|
||||
cover/
|
||||
|
||||
# Translations
|
||||
*.mo
|
||||
*.pot
|
||||
|
||||
# Django stuff:
|
||||
*.log
|
||||
local_settings.py
|
||||
db.sqlite3
|
||||
db.sqlite3-journal
|
||||
|
||||
# Flask stuff:
|
||||
instance/
|
||||
.webassets-cache
|
||||
|
||||
# Scrapy stuff:
|
||||
.scrapy
|
||||
|
||||
# Sphinx documentation
|
||||
docs/_build/
|
||||
|
||||
# PyBuilder
|
||||
.pybuilder/
|
||||
target/
|
||||
|
||||
# Jupyter Notebook
|
||||
.ipynb_checkpoints
|
||||
|
||||
# IPython
|
||||
profile_default/
|
||||
ipython_config.py
|
||||
|
||||
# pyenv
|
||||
# For a library or package, you might want to ignore these files since the code is
|
||||
# intended to run in multiple environments; otherwise, check them in:
|
||||
# .python-version
|
||||
|
||||
# pipenv
|
||||
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
||||
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
||||
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
||||
# install all needed dependencies.
|
||||
#Pipfile.lock
|
||||
|
||||
# UV
|
||||
# Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control.
|
||||
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
||||
# commonly ignored for libraries.
|
||||
#uv.lock
|
||||
|
||||
# poetry
|
||||
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
|
||||
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
||||
# commonly ignored for libraries.
|
||||
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
|
||||
#poetry.lock
|
||||
|
||||
# pdm
|
||||
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
|
||||
#pdm.lock
|
||||
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
|
||||
# in version control.
|
||||
# https://pdm.fming.dev/latest/usage/project/#working-with-version-control
|
||||
.pdm.toml
|
||||
.pdm-python
|
||||
.pdm-build/
|
||||
|
||||
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
|
||||
__pypackages__/
|
||||
|
||||
# Celery stuff
|
||||
celerybeat-schedule
|
||||
celerybeat.pid
|
||||
|
||||
# SageMath parsed files
|
||||
*.sage.py
|
||||
|
||||
# Environments
|
||||
.env
|
||||
.venv
|
||||
env/
|
||||
venv/
|
||||
ENV/
|
||||
env.bak/
|
||||
venv.bak/
|
||||
|
||||
# Spyder project settings
|
||||
.spyderproject
|
||||
.spyproject
|
||||
|
||||
# Rope project settings
|
||||
.ropeproject
|
||||
|
||||
# mkdocs documentation
|
||||
/site
|
||||
|
||||
# mypy
|
||||
.mypy_cache/
|
||||
.dmypy.json
|
||||
dmypy.json
|
||||
|
||||
# Pyre type checker
|
||||
.pyre/
|
||||
|
||||
# pytype static type analyzer
|
||||
.pytype/
|
||||
|
||||
# Cython debug symbols
|
||||
cython_debug/
|
||||
|
||||
# PyCharm
|
||||
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
|
||||
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
|
||||
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
||||
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
||||
#.idea/
|
||||
|
||||
# Ruff stuff:
|
||||
.ruff_cache/
|
||||
|
||||
# PyPI configuration file
|
||||
.pypirc
|
||||
@@ -6,9 +6,9 @@ from time import sleep
|
||||
from PyQt6.QtWidgets import (QApplication, QMainWindow, QWidget, QVBoxLayout,
|
||||
QHBoxLayout, QLabel, QLineEdit, QPushButton,
|
||||
QComboBox, QSpinBox, QDoubleSpinBox, QProgressBar,
|
||||
QScrollArea, QTextEdit, QFileDialog)
|
||||
QScrollArea, QTextEdit, QFileDialog, QMenuBar, QMenu)
|
||||
from PyQt6.QtCore import Qt, QThread, pyqtSignal, QTimer
|
||||
from PyQt6.QtGui import QPixmap, QImage
|
||||
from PyQt6.QtGui import QPixmap, QImage, QAction
|
||||
from PIL import Image
|
||||
from io import BytesIO
|
||||
|
||||
@@ -17,6 +17,9 @@ from modules.console_window import ConsoleWindow
|
||||
from modules.model_manager import ModelManager
|
||||
from modules.sampler_manager import SamplerManager
|
||||
from modules.prompt_window import PromptWindow
|
||||
from modules.negative_prompt_window import NegativePromptWindow
|
||||
from modules.size_preset_manager import SizePresetManager
|
||||
from modules.settings_menu import SettingsMenu
|
||||
|
||||
class GenerationThread(QThread):
|
||||
progress_updated = pyqtSignal(float, str)
|
||||
@@ -64,9 +67,44 @@ class MainWindow(QMainWindow):
|
||||
self.api_url = "http://localhost:7860" # URL по умолчанию
|
||||
self.console_window = None
|
||||
self.prompt_window = None
|
||||
self.negative_prompt_window = None
|
||||
self.progress_timer = None
|
||||
|
||||
# Создаем меню бар
|
||||
self.create_menu_bar()
|
||||
|
||||
self.init_ui()
|
||||
|
||||
def create_menu_bar(self):
|
||||
"""Создание меню бара с настройками"""
|
||||
menubar = QMenuBar(self)
|
||||
menubar.setStyleSheet("""
|
||||
QMenuBar {
|
||||
background-color: #2b2b2b;
|
||||
color: #ffffff;
|
||||
border-bottom: 1px solid #3b3b3b;
|
||||
padding: 2px;
|
||||
}
|
||||
QMenuBar::item {
|
||||
background-color: transparent;
|
||||
padding: 8px 12px;
|
||||
margin: 0;
|
||||
border-radius: 4px;
|
||||
}
|
||||
QMenuBar::item:selected {
|
||||
background-color: #3b3b3b;
|
||||
}
|
||||
QMenuBar::item:pressed {
|
||||
background-color: #404040;
|
||||
}
|
||||
""")
|
||||
|
||||
# Добавляем меню настроек
|
||||
settings_menu = SettingsMenu(self)
|
||||
menubar.addMenu(settings_menu)
|
||||
|
||||
self.setMenuBar(menubar)
|
||||
|
||||
def init_ui(self):
|
||||
self.setWindowTitle("Stable Diffusion UI")
|
||||
self.setStyleSheet("""
|
||||
@@ -164,29 +202,32 @@ class MainWindow(QMainWindow):
|
||||
settings_layout.addLayout(api_layout)
|
||||
|
||||
# Промпт
|
||||
prompt_label = QLabel("Промпт:")
|
||||
self.prompt_input = QTextEdit()
|
||||
self.prompt_input.setPlaceholderText("Введите описание изображения...")
|
||||
prompt_layout = QVBoxLayout()
|
||||
prompt_header = QHBoxLayout()
|
||||
prompt_header.addWidget(prompt_label)
|
||||
|
||||
# Добавляем кнопку промптов рядом с полем промпта
|
||||
prompt_label = QLabel("Промпт:")
|
||||
self.prompt_input = QTextEdit()
|
||||
self.prompt_input.setMinimumHeight(100)
|
||||
prompt_buttons = QHBoxLayout()
|
||||
self.prompt_button = QPushButton("Готовые промпты")
|
||||
self.prompt_button.clicked.connect(self.toggle_prompt_window)
|
||||
prompt_header.addWidget(self.prompt_button)
|
||||
prompt_header.addStretch()
|
||||
|
||||
self.negative_prompt_button = QPushButton("Негативные промпты")
|
||||
self.negative_prompt_button.clicked.connect(self.toggle_negative_prompt_window)
|
||||
prompt_buttons.addWidget(self.prompt_button)
|
||||
prompt_buttons.addWidget(self.negative_prompt_button)
|
||||
prompt_header.addWidget(prompt_label)
|
||||
prompt_header.addLayout(prompt_buttons)
|
||||
prompt_layout.addLayout(prompt_header)
|
||||
prompt_layout.addWidget(self.prompt_input)
|
||||
settings_layout.addLayout(prompt_layout)
|
||||
|
||||
# Негативный промпт
|
||||
neg_prompt_label = QLabel("Негативный промпт:")
|
||||
self.neg_prompt_input = QTextEdit()
|
||||
self.neg_prompt_input.setPlaceholderText("Введите нежелательные элементы...")
|
||||
settings_layout.addWidget(neg_prompt_label)
|
||||
settings_layout.addWidget(self.neg_prompt_input)
|
||||
negative_prompt_layout = QVBoxLayout()
|
||||
negative_prompt_label = QLabel("Негативный промпт:")
|
||||
self.negative_prompt_input = QTextEdit()
|
||||
self.negative_prompt_input.setMinimumHeight(100)
|
||||
negative_prompt_layout.addWidget(negative_prompt_label)
|
||||
negative_prompt_layout.addWidget(self.negative_prompt_input)
|
||||
settings_layout.addLayout(negative_prompt_layout)
|
||||
|
||||
# Добавляем менеджер моделей
|
||||
self.model_manager = ModelManager(self.api_url)
|
||||
@@ -197,6 +238,11 @@ class MainWindow(QMainWindow):
|
||||
self.sampler_manager = SamplerManager(self.api_url)
|
||||
settings_layout.addWidget(self.sampler_manager)
|
||||
|
||||
# Добавляем менеджер пресетов размеров
|
||||
self.size_preset_manager = SizePresetManager()
|
||||
self.size_preset_manager.size_changed.connect(self.on_size_preset_changed)
|
||||
settings_layout.addWidget(self.size_preset_manager)
|
||||
|
||||
# Параметры генерации
|
||||
params_layout = QVBoxLayout()
|
||||
|
||||
@@ -316,12 +362,28 @@ class MainWindow(QMainWindow):
|
||||
else:
|
||||
self.prompt_window.close()
|
||||
|
||||
def add_prompt(self, prompt):
|
||||
def toggle_negative_prompt_window(self):
|
||||
"""Открыть/закрыть окно с негативными промптами"""
|
||||
if self.negative_prompt_window is None:
|
||||
self.negative_prompt_window = NegativePromptWindow(parent=self)
|
||||
self.negative_prompt_window.prompt_selected.connect(self.add_negative_prompt)
|
||||
self.negative_prompt_window.show()
|
||||
else:
|
||||
self.negative_prompt_window.close()
|
||||
|
||||
def add_prompt(self, text):
|
||||
"""Добавить промпт в поле ввода"""
|
||||
current_text = self.prompt_input.toPlainText()
|
||||
if current_text:
|
||||
current_text += ", "
|
||||
self.prompt_input.setText(current_text + prompt)
|
||||
self.prompt_input.setText(current_text + text)
|
||||
|
||||
def add_negative_prompt(self, text):
|
||||
"""Добавить негативный промпт в поле ввода"""
|
||||
current_text = self.negative_prompt_input.toPlainText()
|
||||
if current_text:
|
||||
current_text += ", "
|
||||
self.negative_prompt_input.setText(current_text + text)
|
||||
|
||||
def start_generation(self):
|
||||
self.generate_button.setEnabled(False)
|
||||
@@ -329,7 +391,7 @@ class MainWindow(QMainWindow):
|
||||
|
||||
params = {
|
||||
"prompt": self.prompt_input.toPlainText(),
|
||||
"negative_prompt": self.neg_prompt_input.toPlainText(),
|
||||
"negative_prompt": self.negative_prompt_input.toPlainText(),
|
||||
"width": self.width_input.value(),
|
||||
"height": self.height_input.value(),
|
||||
"seed": self.seed_input.value(),
|
||||
@@ -426,11 +488,20 @@ class MainWindow(QMainWindow):
|
||||
save_button = QPushButton("Сохранить")
|
||||
save_button.clicked.connect(lambda checked, img=img: self.save_image(img))
|
||||
|
||||
# Создаем контейнер для изображения и кнопки
|
||||
# Создаем кнопку копирования в буфер обмена
|
||||
copy_button = QPushButton("Копировать")
|
||||
copy_button.clicked.connect(lambda checked, img=img: self.copy_to_clipboard(img))
|
||||
|
||||
# Создаем контейнер для изображения и кнопок
|
||||
container = QWidget()
|
||||
container_layout = QVBoxLayout(container)
|
||||
container_layout.addWidget(img_label)
|
||||
container_layout.addWidget(save_button)
|
||||
|
||||
# Создаем горизонтальный layout для кнопок
|
||||
buttons_layout = QHBoxLayout()
|
||||
buttons_layout.addWidget(save_button)
|
||||
buttons_layout.addWidget(copy_button)
|
||||
container_layout.addLayout(buttons_layout)
|
||||
|
||||
self.results_content_layout.addWidget(container)
|
||||
self.log_to_console("Изображение успешно добавлено в интерфейс")
|
||||
@@ -450,6 +521,22 @@ class MainWindow(QMainWindow):
|
||||
if file_path:
|
||||
img.save(file_path)
|
||||
|
||||
def copy_to_clipboard(self, img):
|
||||
"""Копирование изображения в буфер обмена"""
|
||||
# Конвертируем изображение в QPixmap
|
||||
buffer = BytesIO()
|
||||
img.save(buffer, format='PNG')
|
||||
buffer.seek(0)
|
||||
pixmap = QPixmap()
|
||||
pixmap.loadFromData(buffer.getvalue())
|
||||
|
||||
# Копируем в буфер обмена
|
||||
clipboard = QApplication.clipboard()
|
||||
clipboard.setPixmap(pixmap)
|
||||
|
||||
# Показываем уведомление
|
||||
self.log_to_console("Изображение скопировано в буфер обмена")
|
||||
|
||||
def update_api_url(self):
|
||||
"""Обновление URL API во всех компонентах"""
|
||||
new_url = self.api_url_input.text()
|
||||
@@ -457,6 +544,12 @@ class MainWindow(QMainWindow):
|
||||
self.sampler_manager.update_api_url(new_url)
|
||||
self.log_to_console(f"API URL обновлен: {new_url}")
|
||||
|
||||
def on_size_preset_changed(self, width, height):
|
||||
"""Обработчик изменения размера через пресет"""
|
||||
self.width_input.setValue(width)
|
||||
self.height_input.setValue(height)
|
||||
self.log_to_console(f"Размер изменен на: {width}x{height}")
|
||||
|
||||
if __name__ == '__main__':
|
||||
app = QApplication(sys.argv)
|
||||
window = MainWindow()
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,134 @@
|
||||
NEGATIVE_PROMPT_CATEGORIES = {
|
||||
"Общие проблемы": {
|
||||
"Качество": [
|
||||
"Низкое качество",
|
||||
"Обычное качество",
|
||||
"Худшее качество",
|
||||
"Низкое разрешение",
|
||||
"Размытое",
|
||||
"Не в фокусе"
|
||||
],
|
||||
"Технические артефакты": [
|
||||
"Логотип",
|
||||
"Текст",
|
||||
"Подпись",
|
||||
"Водяной знак",
|
||||
"Обрезанное",
|
||||
"Монохромное",
|
||||
"Оттенки серого"
|
||||
],
|
||||
"Контент": [
|
||||
"Нецензурный контент",
|
||||
"NSFW",
|
||||
"Удалить взрослых",
|
||||
"Удалить молодых"
|
||||
]
|
||||
},
|
||||
"Анатомия": {
|
||||
"Общие проблемы": [
|
||||
"Неправильная анатомия",
|
||||
"Плохое тело",
|
||||
"Длинное тело",
|
||||
"Длинная шея",
|
||||
"Неправильные пропорции",
|
||||
"Деформированный",
|
||||
"Мутировавший",
|
||||
"Мутация",
|
||||
"Уродливый",
|
||||
"Испорченный"
|
||||
],
|
||||
"Лицо": [
|
||||
"Плохо нарисованное лицо",
|
||||
"Пятна на коже",
|
||||
"Веснушки",
|
||||
"Прыщи",
|
||||
"Косоглазый"
|
||||
],
|
||||
"Конечности": [
|
||||
"Отсутствующая конечность",
|
||||
"Деформированные конечности",
|
||||
"Плавающие конечности",
|
||||
"Отсоединенные конечности",
|
||||
"Дополнительная конечность",
|
||||
"Дополнительные руки",
|
||||
"Мутировавшие руки",
|
||||
"Плохо нарисованные руки",
|
||||
"Деформированные руки",
|
||||
"Мутировавшие руки и пальцы",
|
||||
"Плохие руки",
|
||||
"Отсутствующие пальцы",
|
||||
"Сросшиеся пальцы",
|
||||
"Слишком много пальцев",
|
||||
"Дополнительные ноги",
|
||||
"Плохие ноги"
|
||||
]
|
||||
},
|
||||
"Дополнительные модели": {
|
||||
"Негативные модели": [
|
||||
"AS-YoungV2-neg",
|
||||
"BadDream",
|
||||
"badhandv4",
|
||||
"BadNegAnatomyV1-neg",
|
||||
"EasyNegative",
|
||||
"FastNegativeV2"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
# Словарь для перевода русских названий в английские
|
||||
NEGATIVE_PROMPT_TRANSLATIONS = {
|
||||
"Низкое качество": "low quality",
|
||||
"Обычное качество": "normal quality",
|
||||
"Худшее качество": "worstquality",
|
||||
"Низкое разрешение": "lowres",
|
||||
"Размытое": "blurry",
|
||||
"Не в фокусе": "out of focus",
|
||||
"Логотип": "logo",
|
||||
"Текст": "text",
|
||||
"Подпись": "signature",
|
||||
"Водяной знак": "watermark",
|
||||
"Обрезанное": "cropped",
|
||||
"Монохромное": "monochrome",
|
||||
"Оттенки серого": "grayscale",
|
||||
"Нецензурный контент": "NSFW content",
|
||||
"NSFW": "NSFW",
|
||||
"Удалить взрослых": "(AS-YoungV2-Neg:1.3)",
|
||||
"Удалить молодых": "(AS-Adult-Neg:1.3)",
|
||||
"Неправильная анатомия": "bad anatomy",
|
||||
"Плохое тело": "bad body",
|
||||
"Длинное тело": "long body",
|
||||
"Длинная шея": "long neck",
|
||||
"Неправильные пропорции": "bad proportions",
|
||||
"Деформированный": "deformed",
|
||||
"Мутировавший": "mutated",
|
||||
"Мутация": "mutation",
|
||||
"Уродливый": "ugly",
|
||||
"Испорченный": "disfigured",
|
||||
"Плохо нарисованное лицо": "poorly drawn face",
|
||||
"Пятна на коже": "skin blemishes",
|
||||
"Веснушки": "skin spots",
|
||||
"Прыщи": "acnes",
|
||||
"Косоглазый": "cross-eyed",
|
||||
"Отсутствующая конечность": "missing limb",
|
||||
"Деформированные конечности": "malformed limbs",
|
||||
"Плавающие конечности": "floating limbs",
|
||||
"Отсоединенные конечности": "disconnected limbs",
|
||||
"Дополнительная конечность": "extra limb",
|
||||
"Дополнительные руки": "extra arms",
|
||||
"Мутировавшие руки": "mutated hands",
|
||||
"Плохо нарисованные руки": "poorly drawn hands",
|
||||
"Деформированные руки": "malformed hands",
|
||||
"Мутировавшие руки и пальцы": "mutated hands and fingers",
|
||||
"Плохие руки": "bad hands",
|
||||
"Отсутствующие пальцы": "missing fingers",
|
||||
"Сросшиеся пальцы": "fused fingers",
|
||||
"Слишком много пальцев": "too many fingers",
|
||||
"Дополнительные ноги": "extra legs",
|
||||
"Плохие ноги": "bad feet",
|
||||
"AS-YoungV2-neg": "AS-YoungV2-neg",
|
||||
"BadDream": "BadDream",
|
||||
"badhandv4": "badhandv4",
|
||||
"BadNegAnatomyV1-neg": "BadNegAnatomyV1-neg",
|
||||
"EasyNegative": "EasyNegative",
|
||||
"FastNegativeV2": "FastNegativeV2"
|
||||
}
|
||||
@@ -0,0 +1,200 @@
|
||||
from PyQt6.QtWidgets import (QDialog, QVBoxLayout, QHBoxLayout, QPushButton,
|
||||
QLineEdit, QLabel, QScrollArea, QWidget, QCheckBox, QFrame)
|
||||
from PyQt6.QtCore import Qt, pyqtSignal
|
||||
from .negative_prompt_categories import NEGATIVE_PROMPT_CATEGORIES, NEGATIVE_PROMPT_TRANSLATIONS
|
||||
|
||||
class NegativePromptWindow(QDialog):
|
||||
prompt_selected = pyqtSignal(str)
|
||||
|
||||
def __init__(self, parent=None):
|
||||
super().__init__(parent)
|
||||
self.setWindowTitle("Негативные промпты")
|
||||
self.setModal(False)
|
||||
self.selected_prompts = set()
|
||||
self.setup_ui()
|
||||
self.load_categories()
|
||||
|
||||
def setup_ui(self):
|
||||
self.setStyleSheet("""
|
||||
QDialog {
|
||||
background-color: #2b2b2b;
|
||||
min-width: 1700px;
|
||||
min-height: 800px;
|
||||
}
|
||||
QScrollArea {
|
||||
background-color: #1e1e1e;
|
||||
border: 1px solid #3b3b3b;
|
||||
border-radius: 4px;
|
||||
padding: 8px;
|
||||
}
|
||||
QScrollArea > QWidget > QWidget {
|
||||
background-color: #1e1e1e;
|
||||
}
|
||||
QLabel {
|
||||
color: #ffffff;
|
||||
font-size: 14px;
|
||||
font-weight: bold;
|
||||
padding: 8px;
|
||||
background-color: #3b3b3b;
|
||||
border-radius: 4px;
|
||||
}
|
||||
QCheckBox {
|
||||
color: #ffffff;
|
||||
font-size: 14px;
|
||||
padding: 4px;
|
||||
spacing: 8px;
|
||||
}
|
||||
QCheckBox::indicator {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
}
|
||||
QCheckBox::indicator:unchecked {
|
||||
border: 2px solid #555555;
|
||||
background-color: #3b3b3b;
|
||||
border-radius: 3px;
|
||||
}
|
||||
QCheckBox::indicator:checked {
|
||||
border: 2px solid #dc3545;
|
||||
background-color: #dc3545;
|
||||
border-radius: 3px;
|
||||
}
|
||||
QCheckBox::indicator:hover {
|
||||
border-color: #bb2d3b;
|
||||
}
|
||||
QPushButton {
|
||||
background-color: #dc3545;
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
padding: 8px 16px;
|
||||
font-weight: bold;
|
||||
min-width: 120px;
|
||||
}
|
||||
QPushButton:hover {
|
||||
background-color: #bb2d3b;
|
||||
}
|
||||
QPushButton:pressed {
|
||||
background-color: #a52834;
|
||||
}
|
||||
QLineEdit {
|
||||
background-color: #3b3b3b;
|
||||
color: white;
|
||||
border: 1px solid #555555;
|
||||
border-radius: 4px;
|
||||
padding: 8px;
|
||||
font-size: 14px;
|
||||
min-height: 24px;
|
||||
}
|
||||
QFrame {
|
||||
background-color: #1e1e1e;
|
||||
border: 1px solid #3b3b3b;
|
||||
border-radius: 4px;
|
||||
}
|
||||
""")
|
||||
|
||||
layout = QVBoxLayout(self)
|
||||
|
||||
# Поиск
|
||||
search_layout = QHBoxLayout()
|
||||
search_label = QLabel("Поиск:")
|
||||
self.search_input = QLineEdit()
|
||||
self.search_input.textChanged.connect(self.filter_prompts)
|
||||
search_layout.addWidget(search_label)
|
||||
search_layout.addWidget(self.search_input)
|
||||
layout.addLayout(search_layout)
|
||||
|
||||
# Горизонтальная прокрутка для категорий
|
||||
categories_scroll = QScrollArea()
|
||||
categories_scroll.setWidgetResizable(True)
|
||||
categories_scroll.setHorizontalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOn)
|
||||
categories_scroll.setVerticalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOff)
|
||||
|
||||
categories_widget = QWidget()
|
||||
self.categories_layout = QHBoxLayout(categories_widget)
|
||||
self.categories_layout.setSpacing(10)
|
||||
categories_scroll.setWidget(categories_widget)
|
||||
|
||||
layout.addWidget(categories_scroll)
|
||||
|
||||
# Кнопки
|
||||
buttons_layout = QHBoxLayout()
|
||||
add_button = QPushButton("Добавить выбранные")
|
||||
add_button.clicked.connect(self.add_selected)
|
||||
close_button = QPushButton("Закрыть")
|
||||
close_button.clicked.connect(self.close)
|
||||
buttons_layout.addWidget(add_button)
|
||||
buttons_layout.addWidget(close_button)
|
||||
layout.addLayout(buttons_layout)
|
||||
|
||||
def load_categories(self):
|
||||
"""Загрузка категорий и промптов"""
|
||||
for category, subcategories in NEGATIVE_PROMPT_CATEGORIES.items():
|
||||
# Создаем фрейм для категории
|
||||
category_frame = QFrame()
|
||||
category_layout = QVBoxLayout(category_frame)
|
||||
category_layout.setSpacing(5)
|
||||
|
||||
# Добавляем заголовок категории
|
||||
category_label = QLabel(category)
|
||||
category_layout.addWidget(category_label)
|
||||
|
||||
# Создаем скролл для подкатегорий
|
||||
subcategories_scroll = QScrollArea()
|
||||
subcategories_scroll.setWidgetResizable(True)
|
||||
subcategories_widget = QWidget()
|
||||
subcategories_layout = QVBoxLayout(subcategories_widget)
|
||||
|
||||
for subcategory, prompts in subcategories.items():
|
||||
# Добавляем заголовок подкатегории
|
||||
subcategory_label = QLabel(subcategory)
|
||||
subcategories_layout.addWidget(subcategory_label)
|
||||
|
||||
# Добавляем чекбоксы для промптов
|
||||
for prompt in prompts:
|
||||
checkbox = QCheckBox(prompt)
|
||||
checkbox.stateChanged.connect(lambda state, p=prompt: self.on_checkbox_changed(state, p))
|
||||
subcategories_layout.addWidget(checkbox)
|
||||
|
||||
subcategories_scroll.setWidget(subcategories_widget)
|
||||
category_layout.addWidget(subcategories_scroll)
|
||||
|
||||
# Добавляем категорию в горизонтальный layout
|
||||
self.categories_layout.addWidget(category_frame)
|
||||
|
||||
def on_checkbox_changed(self, state, prompt):
|
||||
"""Обработка изменения состояния чекбокса"""
|
||||
if state == Qt.CheckState.Checked.value:
|
||||
self.selected_prompts.add(prompt)
|
||||
else:
|
||||
self.selected_prompts.discard(prompt)
|
||||
|
||||
def filter_prompts(self, text):
|
||||
"""Фильтрация промптов по поисковому запросу"""
|
||||
text = text.lower()
|
||||
for i in range(self.categories_layout.count()):
|
||||
category_frame = self.categories_layout.itemAt(i).widget()
|
||||
for j in range(category_frame.layout().count()):
|
||||
widget = category_frame.layout().itemAt(j).widget()
|
||||
if isinstance(widget, QScrollArea):
|
||||
scroll_widget = widget.widget()
|
||||
for k in range(scroll_widget.layout().count()):
|
||||
sub_widget = scroll_widget.layout().itemAt(k).widget()
|
||||
if isinstance(sub_widget, QCheckBox):
|
||||
sub_widget.setVisible(text in sub_widget.text().lower())
|
||||
elif isinstance(sub_widget, QLabel):
|
||||
# Показываем/скрываем заголовок подкатегории в зависимости от видимости его промптов
|
||||
has_visible_prompts = False
|
||||
for l in range(k + 1, scroll_widget.layout().count()):
|
||||
next_widget = scroll_widget.layout().itemAt(l).widget()
|
||||
if isinstance(next_widget, QCheckBox) and next_widget.isVisible():
|
||||
has_visible_prompts = True
|
||||
break
|
||||
sub_widget.setVisible(has_visible_prompts)
|
||||
|
||||
def add_selected(self):
|
||||
"""Добавление выбранных промптов"""
|
||||
if self.selected_prompts:
|
||||
# Преобразуем русские названия в английские
|
||||
translated_prompts = [NEGATIVE_PROMPT_TRANSLATIONS.get(prompt, prompt) for prompt in self.selected_prompts]
|
||||
prompts_text = ", ".join(sorted(translated_prompts))
|
||||
self.prompt_selected.emit(prompts_text)
|
||||
@@ -0,0 +1,316 @@
|
||||
PROMPT_TRANSLATIONS = {
|
||||
# Персонажи
|
||||
"Девушка": "girl",
|
||||
"Парень": "boy",
|
||||
"Мужчина": "man",
|
||||
"Женщина": "woman",
|
||||
"Ребенок": "child",
|
||||
"Малыш": "baby",
|
||||
"Пожилой человек": "elderly person",
|
||||
"Старик": "old man",
|
||||
"Старушка": "old woman",
|
||||
|
||||
# Профессии
|
||||
"Художник": "artist",
|
||||
"Музыкант": "musician",
|
||||
"Писатель": "writer",
|
||||
"Учитель": "teacher",
|
||||
"Врач": "doctor",
|
||||
"Инженер": "engineer",
|
||||
"Программист": "programmer",
|
||||
"Повар": "chef",
|
||||
"Полицейский": "police officer",
|
||||
"Пожарный": "firefighter",
|
||||
|
||||
# Животные
|
||||
"Кошка": "cat",
|
||||
"Собака": "dog",
|
||||
"Птица": "bird",
|
||||
"Рыба": "fish",
|
||||
"Лошадь": "horse",
|
||||
"Корова": "cow",
|
||||
"Овца": "sheep",
|
||||
"Коза": "goat",
|
||||
"Кролик": "rabbit",
|
||||
"Хомяк": "hamster",
|
||||
|
||||
# Природа
|
||||
"Гора": "mountain",
|
||||
"Море": "sea",
|
||||
"Океан": "ocean",
|
||||
"Река": "river",
|
||||
"Лес": "forest",
|
||||
"Поле": "field",
|
||||
"Цветок": "flower",
|
||||
"Дерево": "tree",
|
||||
"Трава": "grass",
|
||||
"Песок": "sand",
|
||||
|
||||
# Погода
|
||||
"Солнце": "sun",
|
||||
"Дождь": "rain",
|
||||
"Снег": "snow",
|
||||
"Ветер": "wind",
|
||||
"Туман": "fog",
|
||||
"Облако": "cloud",
|
||||
"Гроза": "thunderstorm",
|
||||
"Радуга": "rainbow",
|
||||
"Мороз": "frost",
|
||||
"Иней": "hoarfrost",
|
||||
|
||||
# Время суток
|
||||
"Утро": "morning",
|
||||
"День": "day",
|
||||
"Вечер": "evening",
|
||||
"Ночь": "night",
|
||||
"Рассвет": "dawn",
|
||||
"Закат": "sunset",
|
||||
"Полдень": "noon",
|
||||
"Полночь": "midnight",
|
||||
|
||||
# Сезоны
|
||||
"Весна": "spring",
|
||||
"Лето": "summer",
|
||||
"Осень": "autumn",
|
||||
"Зима": "winter",
|
||||
|
||||
# Эмоции
|
||||
"Счастье": "happiness",
|
||||
"Грусть": "sadness",
|
||||
"Гнев": "anger",
|
||||
"Страх": "fear",
|
||||
"Удивление": "surprise",
|
||||
"Отвращение": "disgust",
|
||||
"Любовь": "love",
|
||||
"Ненависть": "hate",
|
||||
"Спокойствие": "calmness",
|
||||
"Волнение": "excitement",
|
||||
|
||||
# Действия
|
||||
"Бег": "running",
|
||||
"Ходьба": "walking",
|
||||
"Прыжок": "jumping",
|
||||
"Плавание": "swimming",
|
||||
"Танцы": "dancing",
|
||||
"Пение": "singing",
|
||||
"Игра": "playing",
|
||||
"Работа": "working",
|
||||
"Отдых": "resting",
|
||||
"Сон": "sleeping",
|
||||
|
||||
# Одежда
|
||||
"Платье": "dress",
|
||||
"Рубашка": "shirt",
|
||||
"Брюки": "pants",
|
||||
"Юбка": "skirt",
|
||||
"Куртка": "jacket",
|
||||
"Пальто": "coat",
|
||||
"Шапка": "hat",
|
||||
"Обувь": "shoes",
|
||||
"Перчатки": "gloves",
|
||||
"Шарф": "scarf",
|
||||
|
||||
# Цвета
|
||||
"Красный": "red",
|
||||
"Синий": "blue",
|
||||
"Зеленый": "green",
|
||||
"Желтый": "yellow",
|
||||
"Белый": "white",
|
||||
"Черный": "black",
|
||||
"Серый": "gray",
|
||||
"Коричневый": "brown",
|
||||
"Оранжевый": "orange",
|
||||
"Фиолетовый": "purple",
|
||||
|
||||
# Материалы
|
||||
"Дерево": "wood",
|
||||
"Металл": "metal",
|
||||
"Стекло": "glass",
|
||||
"Пластик": "plastic",
|
||||
"Ткань": "fabric",
|
||||
"Бумага": "paper",
|
||||
"Камень": "stone",
|
||||
"Керамика": "ceramics",
|
||||
"Кожа": "leather",
|
||||
"Резина": "rubber",
|
||||
|
||||
# Технологии
|
||||
"Компьютер": "computer",
|
||||
"Телефон": "phone",
|
||||
"Планшет": "tablet",
|
||||
"Робот": "robot",
|
||||
"Дрон": "drone",
|
||||
"Смартфон": "smartphone",
|
||||
"Ноутбук": "laptop",
|
||||
"Принтер": "printer",
|
||||
"Камера": "camera",
|
||||
"Наушники": "headphones",
|
||||
|
||||
# Транспорт
|
||||
"Машина": "car",
|
||||
"Велосипед": "bicycle",
|
||||
"Мотоцикл": "motorcycle",
|
||||
"Поезд": "train",
|
||||
"Самолет": "airplane",
|
||||
"Корабль": "ship",
|
||||
"Автобус": "bus",
|
||||
"Трамвай": "tram",
|
||||
"Метро": "subway",
|
||||
"Вертолет": "helicopter",
|
||||
|
||||
# Еда
|
||||
"Фрукты": "fruits",
|
||||
"Овощи": "vegetables",
|
||||
"Мясо": "meat",
|
||||
"Рыба": "fish",
|
||||
"Хлеб": "bread",
|
||||
"Молоко": "milk",
|
||||
"Сок": "juice",
|
||||
"Кофе": "coffee",
|
||||
"Чай": "tea",
|
||||
"Сладости": "sweets",
|
||||
|
||||
# Дом
|
||||
"Дом": "house",
|
||||
"Квартира": "apartment",
|
||||
"Комната": "room",
|
||||
"Кухня": "kitchen",
|
||||
"Спальня": "bedroom",
|
||||
"Ванная": "bathroom",
|
||||
"Гостиная": "living room",
|
||||
"Балкон": "balcony",
|
||||
"Подвал": "basement",
|
||||
"Чердак": "attic",
|
||||
|
||||
# Музыка
|
||||
"Гитара": "guitar",
|
||||
"Пианино": "piano",
|
||||
"Скрипка": "violin",
|
||||
"Барабаны": "drums",
|
||||
"Флейта": "flute",
|
||||
"Саксофон": "saxophone",
|
||||
"Труба": "trumpet",
|
||||
"Бас": "bass",
|
||||
"Синтезатор": "synthesizer",
|
||||
"Микрофон": "microphone",
|
||||
|
||||
# Спорт
|
||||
"Футбол": "football",
|
||||
"Баскетбол": "basketball",
|
||||
"Волейбол": "volleyball",
|
||||
"Теннис": "tennis",
|
||||
"Хоккей": "hockey",
|
||||
"Бег": "running",
|
||||
"Плавание": "swimming",
|
||||
"Бокс": "boxing",
|
||||
"Йога": "yoga",
|
||||
"Гимнастика": "gymnastics",
|
||||
|
||||
# Искусство
|
||||
"Картина": "painting",
|
||||
"Рисунок": "drawing",
|
||||
"Скульптура": "sculpture",
|
||||
"Фотография": "photography",
|
||||
"Кино": "movie",
|
||||
"Театр": "theater",
|
||||
"Музыка": "music",
|
||||
"Танец": "dance",
|
||||
"Поэзия": "poetry",
|
||||
"Литература": "literature",
|
||||
|
||||
# Наука
|
||||
"Физика": "physics",
|
||||
"Химия": "chemistry",
|
||||
"Биология": "biology",
|
||||
"Математика": "mathematics",
|
||||
"Астрономия": "astronomy",
|
||||
"Геология": "geology",
|
||||
"Экология": "ecology",
|
||||
"Психология": "psychology",
|
||||
"История": "history",
|
||||
"География": "geography",
|
||||
|
||||
# Время
|
||||
"Секунда": "second",
|
||||
"Минута": "minute",
|
||||
"Час": "hour",
|
||||
"День": "day",
|
||||
"Неделя": "week",
|
||||
"Месяц": "month",
|
||||
"Год": "year",
|
||||
"Век": "century",
|
||||
"Эпоха": "era",
|
||||
"Период": "period",
|
||||
|
||||
# География
|
||||
"Страна": "country",
|
||||
"Город": "city",
|
||||
"Деревня": "village",
|
||||
"Река": "river",
|
||||
"Озеро": "lake",
|
||||
"Море": "sea",
|
||||
"Океан": "ocean",
|
||||
"Гора": "mountain",
|
||||
"Пустыня": "desert",
|
||||
"Джунгли": "jungle",
|
||||
|
||||
# Космос
|
||||
"Солнце": "sun",
|
||||
"Луна": "moon",
|
||||
"Звезда": "star",
|
||||
"Планета": "planet",
|
||||
"Галактика": "galaxy",
|
||||
"Вселенная": "universe",
|
||||
"Астероид": "asteroid",
|
||||
"Комета": "comet",
|
||||
"Спутник": "satellite",
|
||||
"Космический корабль": "spaceship",
|
||||
|
||||
# Фэнтези
|
||||
"Дракон": "dragon",
|
||||
"Единорог": "unicorn",
|
||||
"Фея": "fairy",
|
||||
"Волшебник": "wizard",
|
||||
"Ведьма": "witch",
|
||||
"Гоблин": "goblin",
|
||||
"Эльф": "elf",
|
||||
"Гном": "dwarf",
|
||||
"Орк": "orc",
|
||||
"Тролль": "troll",
|
||||
|
||||
# Мифология
|
||||
"Бог": "god",
|
||||
"Богиня": "goddess",
|
||||
"Герой": "hero",
|
||||
"Миф": "myth",
|
||||
"Легенда": "legend",
|
||||
"Демон": "demon",
|
||||
"Ангел": "angel",
|
||||
"Призрак": "ghost",
|
||||
"Вампир": "vampire",
|
||||
"Оборотень": "werewolf",
|
||||
|
||||
# Стили искусства
|
||||
"Реализм": "realism",
|
||||
"Импрессионизм": "impressionism",
|
||||
"Экспрессионизм": "expressionism",
|
||||
"Сюрреализм": "surrealism",
|
||||
"Абстракционизм": "abstract art",
|
||||
"Поп-арт": "pop art",
|
||||
"Модернизм": "modernism",
|
||||
"Постмодернизм": "postmodernism",
|
||||
"Кубизм": "cubism",
|
||||
"Футуризм": "futurism",
|
||||
|
||||
# Эпохи
|
||||
"Древний мир": "ancient world",
|
||||
"Средневековье": "middle ages",
|
||||
"Ренессанс": "renaissance",
|
||||
"Барокко": "baroque",
|
||||
"Классицизм": "classicism",
|
||||
"Романтизм": "romanticism",
|
||||
"Викторианская эпоха": "victorian era",
|
||||
"Модерн": "art nouveau",
|
||||
"Ар-деко": "art deco",
|
||||
"Современность": "modern era"
|
||||
}
|
||||
@@ -3,6 +3,7 @@ from PyQt6.QtWidgets import (QDialog, QVBoxLayout, QHBoxLayout, QTreeWidget,
|
||||
QScrollArea, QWidget, QCheckBox, QFrame)
|
||||
from PyQt6.QtCore import Qt, pyqtSignal
|
||||
from .prompt_categories import PROMPT_CATEGORIES
|
||||
from .prompt_translations import PROMPT_TRANSLATIONS
|
||||
|
||||
class PromptWindow(QDialog):
|
||||
prompt_selected = pyqtSignal(str)
|
||||
@@ -217,5 +218,7 @@ class PromptWindow(QDialog):
|
||||
def add_selected(self):
|
||||
"""Добавление выбранных промптов"""
|
||||
if self.selected_prompts:
|
||||
prompts_text = ", ".join(sorted(self.selected_prompts))
|
||||
# Преобразуем русские названия в английские
|
||||
translated_prompts = [PROMPT_TRANSLATIONS.get(prompt, prompt) for prompt in self.selected_prompts]
|
||||
prompts_text = ", ".join(sorted(translated_prompts))
|
||||
self.prompt_selected.emit(prompts_text)
|
||||
@@ -0,0 +1,261 @@
|
||||
from PyQt6.QtWidgets import (QMenu,
|
||||
QDialog, QVBoxLayout, QHBoxLayout,
|
||||
QLabel, QLineEdit, QCheckBox,
|
||||
QPushButton, QComboBox, QSpinBox)
|
||||
from PyQt6.QtGui import QIcon, QAction, QActionGroup
|
||||
from PyQt6.QtCore import Qt
|
||||
|
||||
class SettingsMenu(QMenu):
|
||||
def __init__(self, parent=None):
|
||||
super().__init__("⚙️ Настройки", parent)
|
||||
self.setStyleSheet("""
|
||||
QMenu {
|
||||
background-color: #2b2b2b;
|
||||
border: 1px solid #3b3b3b;
|
||||
border-radius: 8px;
|
||||
padding: 5px;
|
||||
}
|
||||
QMenu::item {
|
||||
background-color: transparent;
|
||||
padding: 8px 25px;
|
||||
margin: 2px;
|
||||
border-radius: 4px;
|
||||
color: #ffffff;
|
||||
font-size: 13px;
|
||||
}
|
||||
QMenu::item:selected {
|
||||
background-color: #3b3b3b;
|
||||
}
|
||||
QMenu::separator {
|
||||
height: 1px;
|
||||
background-color: #3b3b3b;
|
||||
margin: 5px 15px;
|
||||
}
|
||||
""")
|
||||
self.setup_menu()
|
||||
|
||||
def setup_menu(self):
|
||||
# API настройки
|
||||
api_action = QAction("🌐 API настройки", self)
|
||||
api_action.triggered.connect(self.show_api_settings)
|
||||
|
||||
# Настройки интерфейса
|
||||
ui_action = QAction("🎨 Настройки интерфейса", self)
|
||||
ui_action.triggered.connect(self.show_ui_settings)
|
||||
|
||||
# Настройки генерации
|
||||
generation_action = QAction("⚡ Настройки генерации", self)
|
||||
generation_action.triggered.connect(self.show_generation_settings)
|
||||
|
||||
# Горячие клавиши
|
||||
hotkeys_action = QAction("⌨️ Горячие клавиши", self)
|
||||
hotkeys_action.triggered.connect(self.show_hotkeys_settings)
|
||||
|
||||
# Добавляем действия в меню
|
||||
self.addAction(api_action)
|
||||
self.addAction(ui_action)
|
||||
self.addAction(generation_action)
|
||||
self.addSeparator()
|
||||
self.addAction(hotkeys_action)
|
||||
|
||||
def show_api_settings(self):
|
||||
dialog = APISettingsDialog(self.parent())
|
||||
dialog.exec()
|
||||
|
||||
def show_ui_settings(self):
|
||||
dialog = UISettingsDialog(self.parent())
|
||||
dialog.exec()
|
||||
|
||||
def show_generation_settings(self):
|
||||
dialog = GenerationSettingsDialog(self.parent())
|
||||
dialog.exec()
|
||||
|
||||
def show_hotkeys_settings(self):
|
||||
dialog = HotkeysSettingsDialog(self.parent())
|
||||
dialog.exec()
|
||||
|
||||
class BaseSettingsDialog(QDialog):
|
||||
def __init__(self, title, parent=None):
|
||||
super().__init__(parent)
|
||||
self.setWindowTitle(title)
|
||||
self.setup_base_ui()
|
||||
|
||||
def setup_base_ui(self):
|
||||
self.setStyleSheet("""
|
||||
QDialog {
|
||||
background-color: #2b2b2b;
|
||||
min-width: 400px;
|
||||
}
|
||||
QLabel {
|
||||
color: #ffffff;
|
||||
font-size: 13px;
|
||||
}
|
||||
QLineEdit, QComboBox, QSpinBox {
|
||||
background-color: #3b3b3b;
|
||||
border: 1px solid #555555;
|
||||
border-radius: 4px;
|
||||
padding: 5px;
|
||||
color: #ffffff;
|
||||
min-width: 200px;
|
||||
}
|
||||
QPushButton {
|
||||
background-color: #0d6efd;
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
padding: 8px 16px;
|
||||
font-weight: bold;
|
||||
min-width: 100px;
|
||||
}
|
||||
QPushButton:hover {
|
||||
background-color: #0b5ed7;
|
||||
}
|
||||
QCheckBox {
|
||||
color: #ffffff;
|
||||
}
|
||||
""")
|
||||
|
||||
self.layout = QVBoxLayout(self)
|
||||
self.layout.setSpacing(15)
|
||||
self.layout.setContentsMargins(20, 20, 20, 20)
|
||||
|
||||
class APISettingsDialog(BaseSettingsDialog):
|
||||
def __init__(self, parent=None):
|
||||
super().__init__("🌐 API настройки", parent)
|
||||
self.setup_ui()
|
||||
|
||||
def setup_ui(self):
|
||||
# API URL
|
||||
url_layout = QHBoxLayout()
|
||||
url_label = QLabel("API URL:")
|
||||
self.url_input = QLineEdit(self.parent().api_url_input.text())
|
||||
url_layout.addWidget(url_label)
|
||||
url_layout.addWidget(self.url_input)
|
||||
self.layout.addLayout(url_layout)
|
||||
|
||||
# Кнопки
|
||||
buttons_layout = QHBoxLayout()
|
||||
save_button = QPushButton("Сохранить")
|
||||
save_button.clicked.connect(self.save_settings)
|
||||
cancel_button = QPushButton("Отмена")
|
||||
cancel_button.clicked.connect(self.reject)
|
||||
buttons_layout.addWidget(save_button)
|
||||
buttons_layout.addWidget(cancel_button)
|
||||
self.layout.addLayout(buttons_layout)
|
||||
|
||||
def save_settings(self):
|
||||
self.parent().api_url_input.setText(self.url_input.text())
|
||||
self.accept()
|
||||
|
||||
class UISettingsDialog(BaseSettingsDialog):
|
||||
def __init__(self, parent=None):
|
||||
super().__init__("🎨 Настройки интерфейса", parent)
|
||||
self.setup_ui()
|
||||
|
||||
def setup_ui(self):
|
||||
# Тема
|
||||
theme_layout = QHBoxLayout()
|
||||
theme_label = QLabel("Тема:")
|
||||
self.theme_combo = QComboBox()
|
||||
self.theme_combo.addItems(["Тёмная", "Светлая"])
|
||||
theme_layout.addWidget(theme_label)
|
||||
theme_layout.addWidget(self.theme_combo)
|
||||
self.layout.addLayout(theme_layout)
|
||||
|
||||
# Размер шрифта
|
||||
font_layout = QHBoxLayout()
|
||||
font_label = QLabel("Размер шрифта:")
|
||||
self.font_spin = QSpinBox()
|
||||
self.font_spin.setRange(8, 24)
|
||||
self.font_spin.setValue(14)
|
||||
font_layout.addWidget(font_label)
|
||||
font_layout.addWidget(self.font_spin)
|
||||
self.layout.addLayout(font_layout)
|
||||
|
||||
# Кнопки
|
||||
buttons_layout = QHBoxLayout()
|
||||
save_button = QPushButton("Сохранить")
|
||||
save_button.clicked.connect(self.save_settings)
|
||||
cancel_button = QPushButton("Отмена")
|
||||
cancel_button.clicked.connect(self.reject)
|
||||
buttons_layout.addWidget(save_button)
|
||||
buttons_layout.addWidget(cancel_button)
|
||||
self.layout.addLayout(buttons_layout)
|
||||
|
||||
def save_settings(self):
|
||||
# TODO: Применить настройки интерфейса
|
||||
self.accept()
|
||||
|
||||
class GenerationSettingsDialog(BaseSettingsDialog):
|
||||
def __init__(self, parent=None):
|
||||
super().__init__("⚡ Настройки генерации", parent)
|
||||
self.setup_ui()
|
||||
|
||||
def setup_ui(self):
|
||||
# Качество по умолчанию
|
||||
quality_layout = QHBoxLayout()
|
||||
quality_label = QLabel("Качество по умолчанию:")
|
||||
self.quality_combo = QComboBox()
|
||||
self.quality_combo.addItems(["Низкое", "Среднее", "Высокое"])
|
||||
quality_layout.addWidget(quality_label)
|
||||
quality_layout.addWidget(self.quality_combo)
|
||||
self.layout.addLayout(quality_layout)
|
||||
|
||||
# Размер по умолчанию
|
||||
size_layout = QHBoxLayout()
|
||||
size_label = QLabel("Размер по умолчанию:")
|
||||
self.size_combo = QComboBox()
|
||||
self.size_combo.addItems(["512x512", "768x512", "512x768"])
|
||||
size_layout.addWidget(size_label)
|
||||
size_layout.addWidget(self.size_combo)
|
||||
self.layout.addLayout(size_layout)
|
||||
|
||||
# Кнопки
|
||||
buttons_layout = QHBoxLayout()
|
||||
save_button = QPushButton("Сохранить")
|
||||
save_button.clicked.connect(self.save_settings)
|
||||
cancel_button = QPushButton("Отмена")
|
||||
cancel_button.clicked.connect(self.reject)
|
||||
buttons_layout.addWidget(save_button)
|
||||
buttons_layout.addWidget(cancel_button)
|
||||
self.layout.addLayout(buttons_layout)
|
||||
|
||||
def save_settings(self):
|
||||
# TODO: Применить настройки генерации
|
||||
self.accept()
|
||||
|
||||
class HotkeysSettingsDialog(BaseSettingsDialog):
|
||||
def __init__(self, parent=None):
|
||||
super().__init__("⌨️ Горячие клавиши", parent)
|
||||
self.setup_ui()
|
||||
|
||||
def setup_ui(self):
|
||||
# Генерация
|
||||
generate_layout = QHBoxLayout()
|
||||
generate_label = QLabel("Генерация:")
|
||||
self.generate_input = QLineEdit("Ctrl+G")
|
||||
generate_layout.addWidget(generate_label)
|
||||
generate_layout.addWidget(self.generate_input)
|
||||
self.layout.addLayout(generate_layout)
|
||||
|
||||
# Открыть консоль
|
||||
console_layout = QHBoxLayout()
|
||||
console_label = QLabel("Открыть консоль:")
|
||||
self.console_input = QLineEdit("Ctrl+`")
|
||||
console_layout.addWidget(console_label)
|
||||
console_layout.addWidget(self.console_input)
|
||||
self.layout.addLayout(console_layout)
|
||||
|
||||
# Кнопки
|
||||
buttons_layout = QHBoxLayout()
|
||||
save_button = QPushButton("Сохранить")
|
||||
save_button.clicked.connect(self.save_settings)
|
||||
cancel_button = QPushButton("Отмена")
|
||||
cancel_button.clicked.connect(self.reject)
|
||||
buttons_layout.addWidget(save_button)
|
||||
buttons_layout.addWidget(cancel_button)
|
||||
self.layout.addLayout(buttons_layout)
|
||||
|
||||
def save_settings(self):
|
||||
# TODO: Применить настройки горячих клавиш
|
||||
self.accept()
|
||||
@@ -0,0 +1,93 @@
|
||||
from PyQt6.QtWidgets import QWidget, QVBoxLayout, QHBoxLayout, QPushButton, QLabel, QFrame
|
||||
from PyQt6.QtCore import Qt, pyqtSignal
|
||||
from .size_presets import SIZE_PRESETS
|
||||
|
||||
class SizePresetManager(QWidget):
|
||||
size_changed = pyqtSignal(int, int) # Сигнал для изменения размера
|
||||
|
||||
def __init__(self, parent=None):
|
||||
super().__init__(parent)
|
||||
self.setup_ui()
|
||||
|
||||
def setup_ui(self):
|
||||
layout = QVBoxLayout(self)
|
||||
layout.setSpacing(2)
|
||||
|
||||
# Заголовок
|
||||
title = QLabel("Размеры:")
|
||||
title.setStyleSheet("""
|
||||
QLabel {
|
||||
color: #ffffff;
|
||||
font-size: 12px;
|
||||
font-weight: bold;
|
||||
padding: 4px;
|
||||
background-color: #3b3b3b;
|
||||
border-radius: 4px;
|
||||
}
|
||||
""")
|
||||
layout.addWidget(title)
|
||||
|
||||
# Создаем фрейм для кнопок
|
||||
buttons_frame = QFrame()
|
||||
buttons_frame.setStyleSheet("""
|
||||
QFrame {
|
||||
background-color: #2b2b2b;
|
||||
border: 1px solid #3b3b3b;
|
||||
border-radius: 4px;
|
||||
padding: 2px;
|
||||
}
|
||||
""")
|
||||
buttons_layout = QVBoxLayout(buttons_frame)
|
||||
buttons_layout.setSpacing(2)
|
||||
|
||||
# Создаем две строки кнопок
|
||||
row1_layout = QHBoxLayout()
|
||||
row2_layout = QHBoxLayout()
|
||||
|
||||
# Разделяем пресеты на две группы
|
||||
presets = list(SIZE_PRESETS.items())
|
||||
half = len(presets) // 2
|
||||
|
||||
# Добавляем кнопки в первую строку
|
||||
for name, (width, height) in presets[:half]:
|
||||
button = self.create_preset_button(name, width, height)
|
||||
row1_layout.addWidget(button)
|
||||
|
||||
# Добавляем кнопки во вторую строку
|
||||
for name, (width, height) in presets[half:]:
|
||||
button = self.create_preset_button(name, width, height)
|
||||
row2_layout.addWidget(button)
|
||||
|
||||
buttons_layout.addLayout(row1_layout)
|
||||
buttons_layout.addLayout(row2_layout)
|
||||
layout.addWidget(buttons_frame)
|
||||
|
||||
def create_preset_button(self, name, width, height):
|
||||
"""Создание кнопки пресета с улучшенным дизайном"""
|
||||
button = QPushButton(name)
|
||||
button.setStyleSheet(f"""
|
||||
QPushButton {{
|
||||
background-color: #3b3b3b;
|
||||
color: white;
|
||||
border: 1px solid #555555;
|
||||
border-radius: 4px;
|
||||
padding: 4px;
|
||||
font-size: 11px;
|
||||
min-width: 80px;
|
||||
text-align: center;
|
||||
}}
|
||||
QPushButton:hover {{
|
||||
background-color: #4a4a4a;
|
||||
border-color: #666666;
|
||||
}}
|
||||
QPushButton:pressed {{
|
||||
background-color: #0d6efd;
|
||||
border-color: #0b5ed7;
|
||||
}}
|
||||
""")
|
||||
button.clicked.connect(lambda checked, w=width, h=height: self.on_preset_clicked(w, h))
|
||||
return button
|
||||
|
||||
def on_preset_clicked(self, width, height):
|
||||
"""Обработчик нажатия на кнопку пресета"""
|
||||
self.size_changed.emit(width, height)
|
||||
@@ -0,0 +1,8 @@
|
||||
SIZE_PRESETS = {
|
||||
"512x768": (512, 768),
|
||||
"768x512": (768, 512),
|
||||
"512x512": (512, 512),
|
||||
"768x432": (768, 432),
|
||||
"432x768": (432, 768),
|
||||
"600x800": (600, 800)
|
||||
}
|
||||
Reference in new issue
Block a user