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)