Skip to content

Commit

Permalink
Use a toggle switch widget in settings dialog
Browse files Browse the repository at this point in the history
  • Loading branch information
Acly committed Dec 18, 2023
1 parent bf63104 commit 5398337
Show file tree
Hide file tree
Showing 2 changed files with 140 additions and 16 deletions.
46 changes: 30 additions & 16 deletions ai_diffusion/ui/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
from .. import util, __version__
from ..connection import ConnectionState, apply_performance_preset
from .server import ServerWidget
from .switch import SwitchWidget
from .theme import add_header, icon, sd_version_icon, red, yellow, green, grey


Expand Down Expand Up @@ -78,7 +79,8 @@ def __init__(self, setting: Setting, parent=None):

self._layout = QHBoxLayout()
self._layout.setContentsMargins(0, 2, 0, 2)
self._layout.addWidget(self._key_label, alignment=Qt.AlignmentFlag.AlignLeft)
self._layout.addWidget(self._key_label)
self._layout.addStretch(1)
self.setLayout(self._layout)

def add_button(self, icon: QIcon, tooltip: str, handler):
Expand Down Expand Up @@ -283,21 +285,34 @@ def value(self, v):
self._edit.setText(v)


class CheckBoxSetting(SettingWidget):
def __init__(self, setting: Setting, text: str, parent=None):
class SwitchSetting(SettingWidget):
_text: tuple[str, str]

def __init__(self, setting: Setting, text=("On", "Off"), parent=None):
super().__init__(setting, parent)
self._checkbox = QCheckBox(self)
self._checkbox.setText(text)
self._checkbox.stateChanged.connect(self._notify_value_changed)
self._layout.addWidget(self._checkbox, alignment=Qt.AlignmentFlag.AlignRight)
self._text = text

self._label = QLabel(text[0], self)
self._switch = SwitchWidget(self)
self._switch.toggled.connect(self._notify_value_changed)
self._layout.addWidget(self._label)
self._layout.addWidget(self._switch)

def _update_text(self):
self._label.setText(self._text[0 if self._switch.is_checked else 1])

def _notify_value_changed(self):
self._update_text()
super()._notify_value_changed()

@property
def value(self):
return self._checkbox.isChecked()
return self._switch.is_checked

@value.setter
def value(self, v):
self._checkbox.setChecked(v)
self._switch.is_checked = v
self._update_text()


class LoraList(QWidget):
Expand Down Expand Up @@ -759,10 +774,7 @@ def add(name: str, widget: QWidget):
self._checkpoint_advanced_widgets = [
add("vae", ComboBoxSetting(StyleSettings.vae, self)),
add("clip_skip", SliderSetting(StyleSettings.clip_skip, self, 1, 12)),
add(
"v_prediction_zsnr",
CheckBoxSetting(StyleSettings.v_prediction_zsnr, "Enable", self),
),
add("v_prediction_zsnr", SwitchSetting(StyleSettings.v_prediction_zsnr, parent=self)),
]
self._toggle_checkpoint_advanced(False)

Expand Down Expand Up @@ -963,9 +975,11 @@ def __init__(self):

S = Settings
self.add("prompt_line_count", SpinBoxSetting(S._prompt_line_count, self, 1, 10))
self.add("show_negative_prompt", CheckBoxSetting(S._show_negative_prompt, "Show", self))
self.add("show_control_end", CheckBoxSetting(S._show_control_end, "Show", self))
self.add("auto_preview", CheckBoxSetting(S._auto_preview, "Enable", self))
self.add(
"show_negative_prompt", SwitchSetting(S._show_negative_prompt, ("Show", "Hide"), self)
)
self.add("show_control_end", SwitchSetting(S._show_control_end, ("Show", "Hide"), self))
self.add("auto_preview", SwitchSetting(S._auto_preview, ("On", "Off"), self))

self._layout.addStretch()

Expand Down
110 changes: 110 additions & 0 deletions ai_diffusion/ui/switch.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
"""Toggle switch widget
from https://stackoverflow.com/a/51825815
"""

from PyQt5.QtCore import QPropertyAnimation, QRectF, QSize, Qt, pyqtProperty # type: ignore
from PyQt5.QtGui import QPainter
from PyQt5.QtWidgets import QAbstractButton, QSizePolicy


class SwitchWidget(QAbstractButton):
def __init__(self, parent=None, track_radius=10, thumb_radius=8):
super().__init__(parent=parent)
self.setCheckable(True)
self.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)

self._track_radius = track_radius
self._thumb_radius = thumb_radius

self._margin = max(0, self._thumb_radius - self._track_radius)
self._base_offset = max(self._thumb_radius, self._track_radius)
self._end_offset = {
True: lambda: self.width() - self._base_offset,
False: lambda: self._base_offset,
}
self._offset = self._base_offset

palette = self.palette()
self._thumb_color = {
True: palette.text(),
False: palette.light(),
}
self._track_color = {
True: palette.highlight(),
False: palette.dark(),
}
self._text_color = {
True: palette.highlight().color(),
False: palette.dark().color(),
}
self._track_opacity = 1

@pyqtProperty(int)
def offset(self): # type: ignore
return self._offset

@offset.setter
def offset(self, value):
self._offset = value
self.update()

def sizeHint(self):
return QSize(
4 * self._track_radius + 2 * self._margin,
2 * self._track_radius + 2 * self._margin,
)

@property
def is_checked(self):
return self.isChecked()

@is_checked.setter
def is_checked(self, checked):
super().setChecked(checked)
self.offset = self._end_offset[checked]()

def resizeEvent(self, event):
super().resizeEvent(event)
self.offset = self._end_offset[self.isChecked()]()

def paintEvent(self, event):
p = QPainter(self)
p.setRenderHint(QPainter.Antialiasing, True)
p.setPen(Qt.PenStyle.NoPen)
track_opacity = self._track_opacity
thumb_opacity = 1.0
if self.isEnabled():
track_brush = self._track_color[self.isChecked()]
thumb_brush = self._thumb_color[self.isChecked()]
else:
track_opacity *= 0.8
track_brush = self.palette().shadow()
thumb_brush = self.palette().mid()

p.setBrush(track_brush)
p.setOpacity(track_opacity)
p.drawRoundedRect(
self._margin,
self._margin,
self.width() - 2 * self._margin,
self.height() - 2 * self._margin,
self._track_radius,
self._track_radius,
)
p.setBrush(thumb_brush)
p.setOpacity(thumb_opacity)
p.drawEllipse(
self.offset - self._thumb_radius,
self._base_offset - self._thumb_radius,
2 * self._thumb_radius,
2 * self._thumb_radius,
)

def mouseReleaseEvent(self, event):
super().mouseReleaseEvent(event)
if event.button() == Qt.MouseButton.LeftButton:
anim = QPropertyAnimation(self, b"offset", self)
anim.setDuration(120)
anim.setStartValue(self.offset)
anim.setEndValue(self._end_offset[self.isChecked()]())
anim.start()

0 comments on commit 5398337

Please sign in to comment.