From 92afc3a90f7f4f3a674417788b815e2ab3f66ca9 Mon Sep 17 00:00:00 2001 From: w4ffl35 <25737761+w4ffl35@users.noreply.github.com> Date: Wed, 9 Oct 2024 11:00:07 -0600 Subject: [PATCH 1/2] improves image input controls --- setup.py | 2 +- ...dd_lock_input_image_column_to_image_to_.py | 34 ++++ src/airunner/data/models/settings_models.py | 2 + src/airunner/enums.py | 1 + src/airunner/styles/dark_theme/styles.qss | 4 + src/airunner/utils/db/column_exists.py | 8 + src/airunner/widgets/canvas/brush_scene.py | 1 + src/airunner/widgets/canvas/custom_scene.py | 21 +-- src/airunner/widgets/canvas/input_image.py | 55 +++++-- .../widgets/canvas/input_image_container.py | 5 + .../widgets/canvas/templates/input_image.ui | 152 +++++++++++++----- .../canvas/templates/input_image_ui.py | 75 ++++++--- .../widgets/switch_widget/switch_widget.py | 118 ++++++++++++++ 13 files changed, 387 insertions(+), 91 deletions(-) create mode 100644 src/airunner/alembic/versions/6d58cbfd61fb_add_lock_input_image_column_to_image_to_.py create mode 100644 src/airunner/utils/db/column_exists.py create mode 100644 src/airunner/widgets/switch_widget/switch_widget.py diff --git a/setup.py b/setup.py index fb811e4a1..2d2ff74bd 100644 --- a/setup.py +++ b/setup.py @@ -2,7 +2,7 @@ setup( name="airunner", - version="3.0.12", + version="3.0.13", author="Capsize LLC", description="A Stable Diffusion GUI", long_description=open("README.md", "r", encoding="utf-8").read(), diff --git a/src/airunner/alembic/versions/6d58cbfd61fb_add_lock_input_image_column_to_image_to_.py b/src/airunner/alembic/versions/6d58cbfd61fb_add_lock_input_image_column_to_image_to_.py new file mode 100644 index 000000000..0c82d6ae3 --- /dev/null +++ b/src/airunner/alembic/versions/6d58cbfd61fb_add_lock_input_image_column_to_image_to_.py @@ -0,0 +1,34 @@ +"""Add lock_input_image column to image_to_image_settings + +Revision ID: 6d58cbfd61fb +Revises: 72d9134823cb +Create Date: 2024-10-09 10:05:11.349862 + +""" +from typing import Sequence, Union + +from alembic import op +import sqlalchemy as sa +from sqlalchemy.dialects import sqlite + +from airunner.utils.db.column_exists import column_exists + +# revision identifiers, used by Alembic. +revision: str = '6d58cbfd61fb' +down_revision: Union[str, None] = '72d9134823cb' +branch_labels: Union[str, Sequence[str], None] = None +depends_on: Union[str, Sequence[str], None] = None + + +def upgrade(): + if not column_exists('image_to_image_settings', 'lock_input_image'): + op.add_column('image_to_image_settings', sa.Column('lock_input_image', sa.Boolean, default=False)) + if not column_exists('controlnet_settings', 'lock_input_image'): + op.add_column('controlnet_settings', sa.Column('lock_input_image', sa.Boolean, default=False)) + +def downgrade(): + if column_exists('image_to_image_settings', 'lock_input_image'): + op.drop_column('image_to_image_settings', 'lock_input_image') + if column_exists('controlnet_settings', 'lock_input_image'): + op.drop_column('image_to_image_settings', 'lock_input_image') + # ### end Alembic commands ### diff --git a/src/airunner/data/models/settings_models.py b/src/airunner/data/models/settings_models.py index 65e2176ed..6aea702c6 100644 --- a/src/airunner/data/models/settings_models.py +++ b/src/airunner/data/models/settings_models.py @@ -91,6 +91,7 @@ class ControlnetSettings(Base): conditioning_scale = Column(Integer, default=100) guidance_scale = Column(Integer, default=750) controlnet = Column(String, default="Canny") + lock_input_image = Column(Boolean, default=False) class ImageToImageSettings(Base): @@ -99,6 +100,7 @@ class ImageToImageSettings(Base): image = Column(String, nullable=True) enabled = Column(Boolean, default=False) use_grid_image_as_input = Column(Boolean, default=False) + lock_input_image = Column(Boolean, default=False) class OutpaintSettings(Base): diff --git a/src/airunner/enums.py b/src/airunner/enums.py index 3ad9de486..2016054ca 100644 --- a/src/airunner/enums.py +++ b/src/airunner/enums.py @@ -242,6 +242,7 @@ class SignalCode(Enum): MASK_LAYER_TOGGLED = enum.auto() MASK_UPDATED = enum.auto() HISTORY_UPDATED = enum.auto() + CANVAS_IMAGE_UPDATED_SIGNAL = enum.auto() class EngineResponseCode(Enum): STATUS = 100 diff --git a/src/airunner/styles/dark_theme/styles.qss b/src/airunner/styles/dark_theme/styles.qss index 9ad5cf1bc..e7212a250 100644 --- a/src/airunner/styles/dark_theme/styles.qss +++ b/src/airunner/styles/dark_theme/styles.qss @@ -730,3 +730,7 @@ QMenu, QToolBar, QMenuBar { QMenu { padding: 10px; } + +SwitchWidget:enabled[checked="true"] { + background-color: rgba(0, 132, 185, 100); +} diff --git a/src/airunner/utils/db/column_exists.py b/src/airunner/utils/db/column_exists.py new file mode 100644 index 000000000..d8afeb143 --- /dev/null +++ b/src/airunner/utils/db/column_exists.py @@ -0,0 +1,8 @@ +from alembic import op +import sqlalchemy as sa + +def column_exists(table_name, column_name): + conn = op.get_bind() + inspector = sa.inspect(conn) + columns = [col['name'] for col in inspector.get_columns(table_name)] + return column_name in columns diff --git a/src/airunner/widgets/canvas/brush_scene.py b/src/airunner/widgets/canvas/brush_scene.py index 49fa3e533..e97109c5b 100644 --- a/src/airunner/widgets/canvas/brush_scene.py +++ b/src/airunner/widgets/canvas/brush_scene.py @@ -217,6 +217,7 @@ def _handle_left_mouse_release(self, event) -> bool: self.emit_signal(SignalCode.GENERATE_MASK) session.commit() session.close() + self.emit_signal(SignalCode.CANVAS_IMAGE_UPDATED_SIGNAL) if self.drawing_pad_settings.mask_layer_enabled: self.initialize_image() self.emit_signal(SignalCode.MASK_UPDATED) diff --git a/src/airunner/widgets/canvas/custom_scene.py b/src/airunner/widgets/canvas/custom_scene.py index 4c7558134..f0cd95474 100644 --- a/src/airunner/widgets/canvas/custom_scene.py +++ b/src/airunner/widgets/canvas/custom_scene.py @@ -117,8 +117,11 @@ def current_active_image(self) -> Image: @current_active_image.setter def current_active_image(self, image: Image): - self._update_current_settings("image", convert_image_to_base64(image)) - self.initialize_image(image) + if image is not None: + image = convert_image_to_base64(image) + self._update_current_settings("image", image) + if self.settings_key == "drawing_pad_settings": + self.emit_signal(SignalCode.CANVAS_IMAGE_UPDATED_SIGNAL) @image_pivot_point.setter def image_pivot_point(self, value): @@ -173,7 +176,7 @@ def on_paste_image_from_clipboard(self): if self.application_settings.resize_on_paste: image = self._resize_image(image) image = convert_image_to_base64(image) - self._update_current_settings("image", image) + self.current_active_image = image self.refresh_image(self.current_active_image) def on_load_image_from_path(self, message): @@ -189,6 +192,7 @@ def on_load_image_signal(self, image_path: str): if self.application_settings.resize_on_paste: image = self._resize_image(image) self.current_active_image = image + self.initialize_image(image) def on_apply_filter_signal(self, message): self._apply_filter(message) @@ -230,7 +234,7 @@ def on_image_generated_signal(self, response): callback(response) def on_canvas_clear_signal(self): - self._update_current_settings("image", None) + self.current_active_image = None self.delete_image() self._clear_history() @@ -269,7 +273,7 @@ def _history_set_image(self, data: dict): self.delete_image() else: self.current_active_image = data["image"] - self.initialize_image() + self.initialize_image(self.current_active_image) def showEvent(self, event): super().showEvent(event) @@ -411,7 +415,7 @@ def delete_image(self): if self.painter and self.painter.isActive(): self.painter.end() - self._update_current_settings("image", None) + self.current_active_image = None self.image = None self.initialize_image() @@ -632,8 +636,7 @@ def _add_image_to_scene( action=GeneratorSection.OUTPAINT.value ) # self._set_current_active_image(image) - base64_image = convert_image_to_base64(image) - self._update_current_settings("image", base64_image) + self.current_active_image = image q_image = ImageQt.ImageQt(image) self.item.setPixmap(QPixmap.fromImage(q_image)) self.item.setZValue(0) @@ -712,7 +715,7 @@ def rotate_image( if image is not None: self._add_image_to_undo() image = image.rotate(angle, expand=True) - self._update_current_settings("image", convert_image_to_base64(image)) + self.current_active_image = image self.initialize_image(image) def _add_undo_history(self, data: dict): diff --git a/src/airunner/widgets/canvas/input_image.py b/src/airunner/widgets/canvas/input_image.py index 8c59a77f0..624ba9d5e 100644 --- a/src/airunner/widgets/canvas/input_image.py +++ b/src/airunner/widgets/canvas/input_image.py @@ -7,6 +7,7 @@ from PySide6.QtGui import QPixmap, QImage, Qt, QPen from PySide6.QtWidgets import QGraphicsScene +from airunner.enums import SignalCode from airunner.settings import VALID_IMAGE_FILES from airunner.utils.convert_base64_to_image import convert_base64_to_image from airunner.utils.convert_image_to_base64 import convert_image_to_base64 @@ -69,32 +70,50 @@ def showEvent(self, event): else: self.ui.mask_blur_slider_widget.hide() - self.ui.enable_checkbox.blockSignals(True) - self.ui.use_grid_image_as_input_checkbox.blockSignals(True) - self.ui.enable_checkbox.setChecked(self.current_settings.enabled) + self.ui.EnableSwitch.toggled.connect(self.enabled_toggled) if self.settings_key == "outpaint_settings": self.ui.import_button.hide() - self.ui.use_grid_image_as_input_checkbox.hide() - self.ui.use_grid_image_as_input_checkbox.hide() + self.ui.link_to_grid_image_button.hide() + self.ui.link_to_grid_image_button.hide() + self.ui.lock_input_image_button.hide() else: - self.ui.use_grid_image_as_input_checkbox.setChecked( + self.ui.EnableSwitch.blockSignals(True) + self.ui.link_to_grid_image_button.blockSignals(True) + self.ui.lock_input_image_button.blockSignals(True) + self.ui.EnableSwitch.checked = self.current_settings.enabled + self.ui.EnableSwitch.setChecked(self.current_settings.enabled) + self.ui.link_to_grid_image_button.setChecked( self.current_settings.use_grid_image_as_input ) - self.ui.enable_checkbox.blockSignals(False) - self.ui.use_grid_image_as_input_checkbox.blockSignals(False) + self.ui.lock_input_image_button.setChecked( + self.current_settings.lock_input_image or False # Provide a default value + ) + self.ui.EnableSwitch.blockSignals(False) + self.ui.link_to_grid_image_button.blockSignals(False) + self.ui.lock_input_image_button.blockSignals(False) + + if self.current_settings.use_grid_image_as_input: + self.load_image_from_grid() + return self.load_image_from_settings() @Slot(bool) def enabled_toggled(self, val): self.update_current_settings("enabled", val) + @Slot(bool) + def lock_input_image(self, val): + self.update_current_settings("lock_input_image", val) + + @Slot(bool) + def refresh_input_image_from_grid(self): + self.load_image_from_grid(forced=True) + @Slot(bool) def use_grid_image_as_input_toggled(self, val): self.update_current_settings("use_grid_image_as_input", val) if val is True: - base64_image = self.drawing_pad_settings.image - self.update_current_settings("image", base64_image) - self.load_image_from_settings() + self.load_image_from_grid() @Slot() def import_clicked(self): @@ -123,6 +142,16 @@ def load_image(self, file_path: str): if image is not None: self.update_current_settings("image", convert_image_to_base64(image)) + def load_image_from_grid(self, forced=False): + if self.settings_key == "outpaint_settings": + return + if not forced and not self.current_settings.use_grid_image_as_input: + return + if not forced and self.current_settings.lock_input_image: + return + self.update_current_settings("image", self.drawing_pad_settings.image) + self.load_image_from_settings() + def load_image_from_settings(self): if self.use_generated_image: image = self.current_settings.generated_image @@ -136,8 +165,8 @@ def load_image_from_settings(self): image = convert_base64_to_image(image) if image is not None: self.load_image_from_object(image) - else: - print("image is none") + return + self.ui.image_container.setScene(None) def load_image_from_object(self, image: Image): if image is None: diff --git a/src/airunner/widgets/canvas/input_image_container.py b/src/airunner/widgets/canvas/input_image_container.py index af82040dc..252b57ffd 100644 --- a/src/airunner/widgets/canvas/input_image_container.py +++ b/src/airunner/widgets/canvas/input_image_container.py @@ -10,12 +10,17 @@ def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.register(SignalCode.MASK_GENERATOR_WORKER_RESPONSE_SIGNAL, self.on_mask_generator_worker_response_signal) self.register(SignalCode.MASK_UPDATED, self.on_mask_generator_worker_response_signal) + self.register(SignalCode.CANVAS_IMAGE_UPDATED_SIGNAL, self.on_load_image_from_grid_signal) self.input_image = None def on_mask_generator_worker_response_signal(self, message): if self.input_image: self.input_image.on_mask_generator_worker_response_signal() + def on_load_image_from_grid_signal(self): + if self.input_image: + self.input_image.load_image_from_grid() + def showEvent(self, event): if self.input_image is None: settings_key = self.settings_key diff --git a/src/airunner/widgets/canvas/templates/input_image.ui b/src/airunner/widgets/canvas/templates/input_image.ui index 27b297622..43c84c65b 100644 --- a/src/airunner/widgets/canvas/templates/input_image.ui +++ b/src/airunner/widgets/canvas/templates/input_image.ui @@ -192,34 +192,59 @@ 10 - - - - 0 - 0 - - + - Use grid image as input + Link to Grid image - Grid Image + + + + + + + true - - - - 0 - 0 - + + + + 24 + 24 + + + + + 24 + 24 + - Enable + Lock current input image - Enable + + + + + + + true + + + + + + + Refresh from grid image + + + + + + @@ -236,6 +261,22 @@ + + + + + 45 + 20 + + + + + 45 + 20 + + + + @@ -279,18 +320,27 @@
airunner/widgets/controlnet/controlnet_settings_widget
1 + + SwitchWidget + QWidget +
airunner/widgets/switch_widget/switch_widget
+ 1 +
- + + + + - use_grid_image_as_input_checkbox - toggled(bool) + import_button + clicked() input_image - use_grid_image_as_input_toggled(bool) + import_clicked() - 108 - 737 + 532 + 557 0 @@ -299,50 +349,66 @@ - enable_checkbox - toggled(bool) + delete_button + clicked() input_image - enabled_toggled(bool) + delete_clicked() - 193 - 737 + 570 + 557 - 4 + 2 748 - import_button - clicked() + link_to_grid_image_button + toggled(bool) input_image - import_clicked() + use_grid_image_as_input_toggled(bool) - 513 - 738 + 29 + 542 - 0 - 748 + -4 + 488 - delete_button + lock_input_image_button + toggled(bool) + input_image + lock_input_image(bool) + + + 58 + 544 + + + -8 + 443 + + + + + pushButton_3 clicked() input_image - delete_clicked() + refresh_input_image_from_grid() - 551 - 738 + 98 + 546 - 2 - 748 + -6 + 384 @@ -352,5 +418,7 @@ delete_clicked() enabled_toggled(bool) use_grid_image_as_input_toggled(bool) + lock_input_image(bool) + refresh_input_image_from_grid() diff --git a/src/airunner/widgets/canvas/templates/input_image_ui.py b/src/airunner/widgets/canvas/templates/input_image_ui.py index d6d9153a2..229d283bd 100644 --- a/src/airunner/widgets/canvas/templates/input_image_ui.py +++ b/src/airunner/widgets/canvas/templates/input_image_ui.py @@ -15,12 +15,15 @@ QFont, QFontDatabase, QGradient, QIcon, QImage, QKeySequence, QLinearGradient, QPainter, QPalette, QPixmap, QRadialGradient, QTransform) -from PySide6.QtWidgets import (QApplication, QCheckBox, QGraphicsView, QGridLayout, - QHBoxLayout, QPushButton, QScrollArea, QSizePolicy, - QSpacerItem, QWidget) +from PySide6.QtWidgets import (QApplication, QGraphicsView, QGridLayout, QHBoxLayout, + QPushButton, QScrollArea, QSizePolicy, QSpacerItem, + QWidget) from airunner.widgets.controlnet.controlnet_settings_widget import ControlnetSettingsWidget from airunner.widgets.slider.slider_widget import SliderWidget +from airunner.widgets.switch_widget.switch_widget import SwitchWidget +import airunner.resources_dark_rc +import airunner.resources_light_rc class Ui_input_image(object): def setupUi(self, input_image): @@ -105,38 +108,53 @@ def setupUi(self, input_image): self.horizontalLayout.setSpacing(10) self.horizontalLayout.setObjectName(u"horizontalLayout") self.horizontalLayout.setContentsMargins(10, -1, 10, 10) - self.use_grid_image_as_input_checkbox = QCheckBox(input_image) - self.use_grid_image_as_input_checkbox.setObjectName(u"use_grid_image_as_input_checkbox") - sizePolicy2 = QSizePolicy(QSizePolicy.Policy.Fixed, QSizePolicy.Policy.Fixed) - sizePolicy2.setHorizontalStretch(0) - sizePolicy2.setVerticalStretch(0) - sizePolicy2.setHeightForWidth(self.use_grid_image_as_input_checkbox.sizePolicy().hasHeightForWidth()) - self.use_grid_image_as_input_checkbox.setSizePolicy(sizePolicy2) + self.link_to_grid_image_button = QPushButton(input_image) + self.link_to_grid_image_button.setObjectName(u"link_to_grid_image_button") + icon = QIcon(QIcon.fromTheme(u"insert-link")) + self.link_to_grid_image_button.setIcon(icon) + self.link_to_grid_image_button.setCheckable(True) - self.horizontalLayout.addWidget(self.use_grid_image_as_input_checkbox) + self.horizontalLayout.addWidget(self.link_to_grid_image_button) - self.enable_checkbox = QCheckBox(input_image) - self.enable_checkbox.setObjectName(u"enable_checkbox") - sizePolicy2.setHeightForWidth(self.enable_checkbox.sizePolicy().hasHeightForWidth()) - self.enable_checkbox.setSizePolicy(sizePolicy2) + self.lock_input_image_button = QPushButton(input_image) + self.lock_input_image_button.setObjectName(u"lock_input_image_button") + self.lock_input_image_button.setMinimumSize(QSize(24, 24)) + self.lock_input_image_button.setMaximumSize(QSize(24, 24)) + icon1 = QIcon(QIcon.fromTheme(u"emblem-readonly")) + self.lock_input_image_button.setIcon(icon1) + self.lock_input_image_button.setCheckable(True) - self.horizontalLayout.addWidget(self.enable_checkbox) + self.horizontalLayout.addWidget(self.lock_input_image_button) + + self.pushButton_3 = QPushButton(input_image) + self.pushButton_3.setObjectName(u"pushButton_3") + icon2 = QIcon(QIcon.fromTheme(u"view-refresh")) + self.pushButton_3.setIcon(icon2) + + self.horizontalLayout.addWidget(self.pushButton_3) self.horizontalSpacer = QSpacerItem(40, 20, QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Minimum) self.horizontalLayout.addItem(self.horizontalSpacer) + self.EnableSwitch = SwitchWidget(input_image) + self.EnableSwitch.setObjectName(u"EnableSwitch") + self.EnableSwitch.setMinimumSize(QSize(45, 20)) + self.EnableSwitch.setMaximumSize(QSize(45, 20)) + + self.horizontalLayout.addWidget(self.EnableSwitch) + self.import_button = QPushButton(input_image) self.import_button.setObjectName(u"import_button") - icon = QIcon(QIcon.fromTheme(u"folder")) - self.import_button.setIcon(icon) + icon3 = QIcon(QIcon.fromTheme(u"folder")) + self.import_button.setIcon(icon3) self.horizontalLayout.addWidget(self.import_button) self.delete_button = QPushButton(input_image) self.delete_button.setObjectName(u"delete_button") - icon1 = QIcon(QIcon.fromTheme(u"user-trash")) - self.delete_button.setIcon(icon1) + icon4 = QIcon(QIcon.fromTheme(u"user-trash")) + self.delete_button.setIcon(icon4) self.horizontalLayout.addWidget(self.delete_button) @@ -145,10 +163,11 @@ def setupUi(self, input_image): self.retranslateUi(input_image) - self.use_grid_image_as_input_checkbox.toggled.connect(input_image.use_grid_image_as_input_toggled) - self.enable_checkbox.toggled.connect(input_image.enabled_toggled) self.import_button.clicked.connect(input_image.import_clicked) self.delete_button.clicked.connect(input_image.delete_clicked) + self.link_to_grid_image_button.toggled.connect(input_image.use_grid_image_as_input_toggled) + self.lock_input_image_button.toggled.connect(input_image.lock_input_image) + self.pushButton_3.clicked.connect(input_image.refresh_input_image_from_grid) QMetaObject.connectSlotsByName(input_image) # setupUi @@ -160,13 +179,17 @@ def retranslateUi(self, input_image): self.mask_blur_slider_widget.setProperty("label_text", QCoreApplication.translate("input_image", u"Mask Blur", None)) self.mask_blur_slider_widget.setProperty("settings_property", QCoreApplication.translate("input_image", u"outpaint_settings.mask_blur", None)) #if QT_CONFIG(tooltip) - self.use_grid_image_as_input_checkbox.setToolTip(QCoreApplication.translate("input_image", u"Use grid image as input", None)) + self.link_to_grid_image_button.setToolTip(QCoreApplication.translate("input_image", u"Link to Grid image", None)) +#endif // QT_CONFIG(tooltip) + self.link_to_grid_image_button.setText("") +#if QT_CONFIG(tooltip) + self.lock_input_image_button.setToolTip(QCoreApplication.translate("input_image", u"Lock current input image", None)) #endif // QT_CONFIG(tooltip) - self.use_grid_image_as_input_checkbox.setText(QCoreApplication.translate("input_image", u"Grid Image", None)) + self.lock_input_image_button.setText("") #if QT_CONFIG(tooltip) - self.enable_checkbox.setToolTip(QCoreApplication.translate("input_image", u"Enable", None)) + self.pushButton_3.setToolTip(QCoreApplication.translate("input_image", u"Refresh from grid image", None)) #endif // QT_CONFIG(tooltip) - self.enable_checkbox.setText(QCoreApplication.translate("input_image", u"Enable ", None)) + self.pushButton_3.setText("") #if QT_CONFIG(tooltip) self.import_button.setToolTip(QCoreApplication.translate("input_image", u"Import", None)) #endif // QT_CONFIG(tooltip) diff --git a/src/airunner/widgets/switch_widget/switch_widget.py b/src/airunner/widgets/switch_widget/switch_widget.py new file mode 100644 index 000000000..a8998444c --- /dev/null +++ b/src/airunner/widgets/switch_widget/switch_widget.py @@ -0,0 +1,118 @@ +from PySide6.QtCore import QObject, QPropertyAnimation, QEasingCurve, Property, QPointF, Slot, QSize, Signal +from PySide6.QtGui import QGradient, QLinearGradient, QPalette, Qt, QPainter, QColor +from PySide6.QtWidgets import QAbstractButton, QApplication + +class SwitchPrivate(QObject): + def __init__(self, q, parent=None): + QObject.__init__(self, parent=parent) + self.mPointer = q + self.mPosition = 0.0 + self.mGradient = QLinearGradient() + self.mGradient.setSpread(QGradient.Spread.PadSpread) + self.checked = False + + self.animation = QPropertyAnimation(self) + self.animation.setTargetObject(self) + self.animation.setPropertyName(b'position') + self.animation.setStartValue(0.0) + self.animation.setEndValue(1.0) + self.animation.setDuration(200) + self.animation.setEasingCurve(QEasingCurve.Type.InOutExpo) + + self.animation.finished.connect(self.mPointer.update) + + @Property(float) + def position(self): + return self.mPosition + + @position.setter + def position(self, value): + self.mPosition = value + self.mPointer.update() + + def draw(self, painter): + r = self.mPointer.rect() + margin = r.height() / 10 + shadow = self.mPointer.palette().color(QPalette.ColorRole.Shadow) + light = self.mPointer.palette().color(QPalette.ColorRole.Light) + button = self.mPointer.palette().color(QPalette.ColorRole.Button) + painter.setPen(Qt.PenStyle.NoPen) + + if self.mPointer.isEnabled() and self.checked: + self.mGradient.setColorAt(0, Qt.GlobalColor.blue) + self.mGradient.setColorAt(1, Qt.GlobalColor.blue) + else: + self.mGradient.setColorAt(0, shadow.darker(130)) + self.mGradient.setColorAt(1, light.darker(130)) + + self.mGradient.setStart(0, r.height()) + self.mGradient.setFinalStop(0, 0) + painter.setBrush(self.mGradient) + painter.drawRoundedRect(r, r.height() / 2, r.height() / 2) + + self.mGradient.setColorAt(0, shadow.darker(140)) + self.mGradient.setColorAt(1, light.darker(160)) + self.mGradient.setStart(0, 0) + self.mGradient.setFinalStop(0, r.height()) + painter.setBrush(self.mGradient) + painter.drawRoundedRect(r.adjusted(margin, margin, -margin, -margin), r.height() / 2, r.height() / 2) + + self.mGradient.setColorAt(0, button.darker(130)) + self.mGradient.setColorAt(1, button) + + painter.setBrush(self.mGradient) + + x = r.height() / 2.0 + self.mPosition * (r.width() - r.height()) + painter.drawEllipse(QPointF(x, r.height() / 2), r.height() / 2 - margin, r.height() / 2 - margin) + + @Slot(bool, name='animate') + def animate(self, checked): + self.checked = checked + self.animation.setDirection(QPropertyAnimation.Direction.Forward if checked else QPropertyAnimation.Direction.Backward) + self.animation.start() + +class SwitchWidget(QAbstractButton): + toggled = Signal(bool) + + def __init__(self, parent=None): + QAbstractButton.__init__(self, parent=parent) + self.dPtr = SwitchPrivate(self) + self.setCheckable(True) + self.clicked.connect(self.dPtr.animate) + self.clicked.connect(self.emitToggled) + self._backgroundColor = QColor("blue") # Initialize the internal attribute + + # Initialize the checked state + self.setChecked(True) + self.dPtr.animate(True) + + def emitToggled(self, checked): + self.toggled.emit(checked) + + def sizeHint(self): + return QSize(84, 42) + + def paintEvent(self, event): + painter = QPainter(self) + painter.setRenderHint(QPainter.RenderHint.Antialiasing) + self.dPtr.draw(painter) + + def resizeEvent(self, event): + self.update() + + @Property(QColor) + def backgroundColor(self): + return self._backgroundColor # Return the internal attribute + + @backgroundColor.setter + def backgroundColor(self, color): + self._backgroundColor = color # Set the internal attribute + self.update() + +if __name__ == '__main__': + import sys + app = QApplication(sys.argv) + w = SwitchWidget() + w.setProperty("backgroundColor", QColor("blue")) + w.show() + sys.exit(app.exec_()) From 9ca2aa2fd6069e1e92ab4d923035f7f888b7ab27 Mon Sep 17 00:00:00 2001 From: w4ffl35 <25737761+w4ffl35@users.noreply.github.com> Date: Wed, 9 Oct 2024 11:18:46 -0600 Subject: [PATCH 2/2] fixes controlnet input image --- src/airunner/widgets/canvas/input_image_container.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/airunner/widgets/canvas/input_image_container.py b/src/airunner/widgets/canvas/input_image_container.py index 252b57ffd..68015ff6b 100644 --- a/src/airunner/widgets/canvas/input_image_container.py +++ b/src/airunner/widgets/canvas/input_image_container.py @@ -12,6 +12,7 @@ def __init__(self, *args, **kwargs): self.register(SignalCode.MASK_UPDATED, self.on_mask_generator_worker_response_signal) self.register(SignalCode.CANVAS_IMAGE_UPDATED_SIGNAL, self.on_load_image_from_grid_signal) self.input_image = None + self.generated_image = None def on_mask_generator_worker_response_signal(self, message): if self.input_image: @@ -29,8 +30,8 @@ def showEvent(self, event): label = "Image-to-Image" if settings_key == "controlnet_settings": label = "Controlnet" - self.input_image = InputImage(settings_key=self.settings_key, use_generated_image=True) - self.ui.tabWidget.addTab(self.input_image, "Generated Image") + self.generated_image = InputImage(settings_key=self.settings_key, use_generated_image=True) + self.ui.tabWidget.addTab(self.generated_image, "Generated Image") elif settings_key == "outpaint_settings": label = "Inpaint / Outpaint" self.ui.label.setText(label)