3 changed files with 372 additions and 17 deletions

No files matched your search

+38 -17
View File
@@ -17,6 +17,7 @@ from modules.console_window import ConsoleWindow
from modules.model_manager import ModelManager from modules.model_manager import ModelManager
from modules.sampler_manager import SamplerManager from modules.sampler_manager import SamplerManager
from modules.prompt_window import PromptWindow from modules.prompt_window import PromptWindow
from modules.negative_prompt_window import NegativePromptWindow
class GenerationThread(QThread): class GenerationThread(QThread):
progress_updated = pyqtSignal(float, str) progress_updated = pyqtSignal(float, str)
@@ -64,6 +65,7 @@ class MainWindow(QMainWindow):
self.api_url = "http://localhost:7860" # URL по умолчанию self.api_url = "http://localhost:7860" # URL по умолчанию
self.console_window = None self.console_window = None
self.prompt_window = None self.prompt_window = None
self.negative_prompt_window = None
self.progress_timer = None self.progress_timer = None
self.init_ui() self.init_ui()
@@ -164,29 +166,32 @@ class MainWindow(QMainWindow):
settings_layout.addLayout(api_layout) settings_layout.addLayout(api_layout)
# Промпт # Промпт
prompt_label = QLabel("Промпт:")
self.prompt_input = QTextEdit()
self.prompt_input.setPlaceholderText("Введите описание изображения...")
prompt_layout = QVBoxLayout() prompt_layout = QVBoxLayout()
prompt_header = QHBoxLayout() 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 = QPushButton("Готовые промпты")
self.prompt_button.clicked.connect(self.toggle_prompt_window) self.prompt_button.clicked.connect(self.toggle_prompt_window)
prompt_header.addWidget(self.prompt_button) self.negative_prompt_button = QPushButton("Негативные промпты")
prompt_header.addStretch() 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.addLayout(prompt_header)
prompt_layout.addWidget(self.prompt_input) prompt_layout.addWidget(self.prompt_input)
settings_layout.addLayout(prompt_layout) settings_layout.addLayout(prompt_layout)
# Негативный промпт # Негативный промпт
neg_prompt_label = QLabel("Негативный промпт:") negative_prompt_layout = QVBoxLayout()
self.neg_prompt_input = QTextEdit() negative_prompt_label = QLabel("Негативный промпт:")
self.neg_prompt_input.setPlaceholderText("Введите нежелательные элементы...") self.negative_prompt_input = QTextEdit()
settings_layout.addWidget(neg_prompt_label) self.negative_prompt_input.setMinimumHeight(100)
settings_layout.addWidget(self.neg_prompt_input) 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) self.model_manager = ModelManager(self.api_url)
@@ -316,12 +321,28 @@ class MainWindow(QMainWindow):
else: else:
self.prompt_window.close() 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() current_text = self.prompt_input.toPlainText()
if current_text: if current_text:
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): def start_generation(self):
self.generate_button.setEnabled(False) self.generate_button.setEnabled(False)
@@ -329,7 +350,7 @@ class MainWindow(QMainWindow):
params = { params = {
"prompt": self.prompt_input.toPlainText(), "prompt": self.prompt_input.toPlainText(),
"negative_prompt": self.neg_prompt_input.toPlainText(), "negative_prompt": self.negative_prompt_input.toPlainText(),
"width": self.width_input.value(), "width": self.width_input.value(),
"height": self.height_input.value(), "height": self.height_input.value(),
"seed": self.seed_input.value(), "seed": self.seed_input.value(),
+134
View File
@@ -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"
}
+200
View File
@@ -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: 1500px;
min-height: 700px;
}
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)