mirror of
https://github.com/invoke-ai/InvokeAI
synced 2024-08-30 20:32:17 +00:00
convert existing model display to column format
This commit is contained in:
parent
7545e38655
commit
f299f40763
@ -7,6 +7,7 @@
|
|||||||
# Coauthor: Kevin Turner http://github.com/keturn
|
# Coauthor: Kevin Turner http://github.com/keturn
|
||||||
#
|
#
|
||||||
import argparse
|
import argparse
|
||||||
|
import curses
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
import shutil
|
import shutil
|
||||||
@ -22,6 +23,7 @@ import npyscreen
|
|||||||
import requests
|
import requests
|
||||||
from diffusers import AutoencoderKL
|
from diffusers import AutoencoderKL
|
||||||
from huggingface_hub import hf_hub_url
|
from huggingface_hub import hf_hub_url
|
||||||
|
from npyscreen import widget
|
||||||
from omegaconf import OmegaConf
|
from omegaconf import OmegaConf
|
||||||
from omegaconf.dictconfig import DictConfig
|
from omegaconf.dictconfig import DictConfig
|
||||||
from tqdm import tqdm
|
from tqdm import tqdm
|
||||||
@ -30,6 +32,7 @@ import invokeai.configs as configs
|
|||||||
from ldm.invoke.devices import choose_precision, choose_torch_device
|
from ldm.invoke.devices import choose_precision, choose_torch_device
|
||||||
from ldm.invoke.generator.diffusers_pipeline import StableDiffusionGeneratorPipeline
|
from ldm.invoke.generator.diffusers_pipeline import StableDiffusionGeneratorPipeline
|
||||||
from ldm.invoke.globals import Globals, global_cache_dir, global_config_dir
|
from ldm.invoke.globals import Globals, global_cache_dir, global_config_dir
|
||||||
|
from ldm.invoke.config.widgets import MultiSelectColumns
|
||||||
|
|
||||||
warnings.filterwarnings("ignore")
|
warnings.filterwarnings("ignore")
|
||||||
import torch
|
import torch
|
||||||
@ -84,37 +87,52 @@ class addModelsForm(npyscreen.FormMultiPageAction):
|
|||||||
self.starter_model_list = [
|
self.starter_model_list = [
|
||||||
x for x in list(self.initial_models.keys()) if x not in self.existing_models
|
x for x in list(self.initial_models.keys()) if x not in self.existing_models
|
||||||
]
|
]
|
||||||
|
self.installed_models=dict()
|
||||||
super().__init__(parentApp, name)
|
super().__init__(parentApp, name)
|
||||||
|
|
||||||
def create(self):
|
def create(self):
|
||||||
starter_model_labels = [
|
window_height, window_width = curses.initscr().getmaxyx()
|
||||||
"%-30s %-50s" % (x, self.initial_models[x].description)
|
starter_model_labels = self._get_starter_model_labels()
|
||||||
for x in self.starter_model_list
|
|
||||||
]
|
|
||||||
recommended_models = [
|
recommended_models = [
|
||||||
x
|
x
|
||||||
for x in self.starter_model_list
|
for x in self.starter_model_list
|
||||||
if self.initial_models[x].get("recommended", False)
|
if self.initial_models[x].get("recommended", False)
|
||||||
]
|
]
|
||||||
previously_installed_models = [
|
previously_installed_models = sorted(
|
||||||
x for x in list(self.initial_models.keys()) if x in self.existing_models
|
[
|
||||||
]
|
x for x in list(self.initial_models.keys()) if x in self.existing_models
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
if len(previously_installed_models) > 0:
|
||||||
|
title = self.add_widget_intelligent(
|
||||||
|
npyscreen.TitleText,
|
||||||
|
name="Currently installed starter models. Uncheck to delete:",
|
||||||
|
editable=False,
|
||||||
|
color="CONTROL",
|
||||||
|
)
|
||||||
|
self.nextrely -= 1
|
||||||
|
columns = 3
|
||||||
|
self.previously_installed_models = self.add_widget_intelligent(
|
||||||
|
MultiSelectColumns,
|
||||||
|
columns=columns,
|
||||||
|
values=previously_installed_models,
|
||||||
|
value=[x for x in range(0,len(previously_installed_models))],
|
||||||
|
max_height=len(previously_installed_models)+1 // columns,
|
||||||
|
slow_scroll=True,
|
||||||
|
scroll_exit = True,
|
||||||
|
)
|
||||||
self.add_widget_intelligent(
|
self.add_widget_intelligent(
|
||||||
npyscreen.TitleText,
|
npyscreen.TitleText,
|
||||||
name="This is a starter set of Stable Diffusion models from HuggingFace",
|
name="Select from a starter set of Stable Diffusion models from HuggingFace:",
|
||||||
editable=False,
|
editable=False,
|
||||||
color="CONTROL",
|
color="CONTROL",
|
||||||
)
|
)
|
||||||
self.add_widget_intelligent(
|
self.nextrely -= 2
|
||||||
npyscreen.FixedText,
|
|
||||||
value="Select models to install:",
|
|
||||||
editable=False,
|
|
||||||
color="LABELBOLD",
|
|
||||||
)
|
|
||||||
self.add_widget_intelligent(npyscreen.FixedText, value="", editable=False),
|
self.add_widget_intelligent(npyscreen.FixedText, value="", editable=False),
|
||||||
self.models_selected = self.add_widget_intelligent(
|
self.models_selected = self.add_widget_intelligent(
|
||||||
npyscreen.MultiSelect,
|
npyscreen.MultiSelect,
|
||||||
name="Install/Remove Models",
|
name="Install Starter Models",
|
||||||
values=starter_model_labels,
|
values=starter_model_labels,
|
||||||
value=[
|
value=[
|
||||||
self.starter_model_list.index(x)
|
self.starter_model_list.index(x)
|
||||||
@ -124,72 +142,80 @@ class addModelsForm(npyscreen.FormMultiPageAction):
|
|||||||
max_height=len(starter_model_labels) + 1,
|
max_height=len(starter_model_labels) + 1,
|
||||||
scroll_exit=True,
|
scroll_exit=True,
|
||||||
)
|
)
|
||||||
if len(previously_installed_models) > 0:
|
for line in [
|
||||||
title = self.add_widget_intelligent(
|
'Import checkpoint/safetensor models from the directory below.',
|
||||||
npyscreen.TitleText,
|
'(Use <tab> to autocomplete)'
|
||||||
name=f"These starter models are already installed. Use the command-line or Web UIs to manage them:",
|
]:
|
||||||
editable=False,
|
|
||||||
color="CONTROL",
|
|
||||||
)
|
|
||||||
y_origin = title.rely+1
|
|
||||||
|
|
||||||
# use three columns
|
|
||||||
col_cnt = 3
|
|
||||||
col_width = max([len(x) for x in previously_installed_models])+2
|
|
||||||
rows = ceil(len(previously_installed_models)/col_cnt)
|
|
||||||
previously_installed_models = sorted(previously_installed_models)
|
|
||||||
|
|
||||||
for i in range(0,len(previously_installed_models)):
|
|
||||||
m = previously_installed_models[i]
|
|
||||||
row = i % rows
|
|
||||||
col = i // rows
|
|
||||||
self.add_widget_intelligent(
|
|
||||||
npyscreen.FixedText,
|
|
||||||
value=m,
|
|
||||||
editable=False,
|
|
||||||
relx=col_cnt+col*col_width,
|
|
||||||
rely=y_origin+row
|
|
||||||
)
|
|
||||||
self.nextrely += rows
|
|
||||||
self.autoload_directory = self.add_widget_intelligent(
|
|
||||||
npyscreen.TitleFilename,
|
|
||||||
name='Import all .ckpt/.safetensors files from this directory (<tab> to autocomplete):',
|
|
||||||
select_dir=True,
|
|
||||||
must_exist=True,
|
|
||||||
use_two_lines=False,
|
|
||||||
begin_entry_at=81,
|
|
||||||
value=os.path.expanduser('~'+'/'),
|
|
||||||
scroll_exit=True,
|
|
||||||
)
|
|
||||||
self.autoload_onstartup = self.add_widget_intelligent(
|
|
||||||
npyscreen.Checkbox,
|
|
||||||
name='Scan this directory each time InvokeAI starts for new models to import.',
|
|
||||||
value=False,
|
|
||||||
scroll_exit=True,
|
|
||||||
)
|
|
||||||
self.nextrely += 1
|
|
||||||
self.add_widget_intelligent(
|
self.add_widget_intelligent(
|
||||||
npyscreen.TitleText,
|
npyscreen.TitleText,
|
||||||
name='In the space below, you may cut and paste URLs, paths to .ckpt/.safetensor files, or HuggingFace diffusers repository names to import:',
|
name=line,
|
||||||
editable=False,
|
editable=False,
|
||||||
color="CONTROL",
|
color="CONTROL",
|
||||||
)
|
)
|
||||||
self.model_names = self.add_widget_intelligent(
|
self.nextrely -= 1
|
||||||
npyscreen.MultiLineEdit,
|
self.autoload_directory = self.add_widget_intelligent(
|
||||||
max_width=75,
|
npyscreen.TitleFilename,
|
||||||
max_height=16,
|
name='Directory:',
|
||||||
scroll_exit=True,
|
select_dir=True,
|
||||||
relx=18
|
must_exist=True,
|
||||||
)
|
use_two_lines=False,
|
||||||
self.autoload_onstartup = self.add_widget_intelligent(
|
value=os.path.expanduser('~'+'/'),
|
||||||
npyscreen.TitleSelectOne,
|
scroll_exit=True,
|
||||||
name='Keep files in original format, or convert .ckpt/.safetensors into fast-loading diffusers models:',
|
)
|
||||||
values=['Original format','Convert to diffusers format'],
|
self.autoload_onstartup = self.add_widget_intelligent(
|
||||||
value=0,
|
npyscreen.Checkbox,
|
||||||
scroll_exit=True,
|
name='Scan this directory each time InvokeAI starts for new models to import.',
|
||||||
|
value=False,
|
||||||
|
scroll_exit=True,
|
||||||
|
)
|
||||||
|
self.nextrely += 1
|
||||||
|
for line in [
|
||||||
|
'In the space below, you may cut and paste URLs, paths to .ckpt/.safetensor files',
|
||||||
|
'or HuggingFace diffusers repository names to import.',
|
||||||
|
'(Use control-V or shift-control-V to paste):'
|
||||||
|
]:
|
||||||
|
self.add_widget_intelligent(
|
||||||
|
npyscreen.TitleText,
|
||||||
|
name=line,
|
||||||
|
editable=False,
|
||||||
|
color="CONTROL",
|
||||||
)
|
)
|
||||||
|
self.nextrely -= 1
|
||||||
|
self.model_names = self.add_widget_intelligent(
|
||||||
|
npyscreen.MultiLineEdit,
|
||||||
|
max_width=75,
|
||||||
|
max_height=8,
|
||||||
|
scroll_exit=True,
|
||||||
|
relx=3
|
||||||
|
)
|
||||||
|
self.autoload_onstartup = self.add_widget_intelligent(
|
||||||
|
npyscreen.TitleSelectOne,
|
||||||
|
name='Keep files in original format, or convert .ckpt/.safetensors into fast-loading diffusers models:',
|
||||||
|
values=['Original format','Convert to diffusers format'],
|
||||||
|
value=0,
|
||||||
|
scroll_exit=True,
|
||||||
|
)
|
||||||
|
self.find_next_editable()
|
||||||
|
# self.set_editing(self.models_selected)
|
||||||
|
# self.display()
|
||||||
|
# self.models_selected.editing=True
|
||||||
|
# self.models_selected.edit()
|
||||||
|
|
||||||
self.models_selected.editing = True
|
def _get_starter_model_labels(self):
|
||||||
|
window_height, window_width = curses.initscr().getmaxyx()
|
||||||
|
label_width = 25
|
||||||
|
checkbox_width = 4
|
||||||
|
spacing_width = 2
|
||||||
|
description_width = window_width - label_width - checkbox_width - spacing_width
|
||||||
|
im = self.initial_models
|
||||||
|
names = list(im.keys())
|
||||||
|
descriptions = [im[x].description [0:description_width-3]+'...'
|
||||||
|
if len(im[x].description) > description_width
|
||||||
|
else im[x].description
|
||||||
|
for x in im]
|
||||||
|
return [
|
||||||
|
f"%-{label_width}s %s" % (names[x], descriptions[x]) for x in range(0,len(im))
|
||||||
|
]
|
||||||
|
|
||||||
def on_ok(self):
|
def on_ok(self):
|
||||||
self.parentApp.setNextForm(None)
|
self.parentApp.setNextForm(None)
|
||||||
@ -607,12 +633,23 @@ def main():
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
select_and_download_models(opt)
|
select_and_download_models(opt)
|
||||||
|
except AssertionError as e:
|
||||||
|
print(str(e))
|
||||||
|
sys.exit(-1)
|
||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
print("\nGoodbye! Come back soon.")
|
print("\nGoodbye! Come back soon.")
|
||||||
except Exception as e:
|
except (widget.NotEnoughSpaceForWidget, Exception) as e:
|
||||||
print(f'\nA problem occurred during initialization.\nThe error was: "{str(e)}"')
|
if str(e).startswith("Height of 1 allocated"):
|
||||||
print(traceback.format_exc())
|
print(
|
||||||
|
"** Insufficient vertical space for the interface. Please make your window taller and try again"
|
||||||
|
)
|
||||||
|
elif str(e).startswith('addwstr'):
|
||||||
|
print(
|
||||||
|
'** Insufficient horizontal space for the interface. Please make your window wider and try again.'
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
print(f"** A layout error has occurred: {str(e)}")
|
||||||
|
sys.exit(-1)
|
||||||
|
|
||||||
# -------------------------------------
|
# -------------------------------------
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
70
ldm/invoke/config/widgets.py
Normal file
70
ldm/invoke/config/widgets.py
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
'''
|
||||||
|
Widget class definitions used by model_select.py, merge_diffusers.py and textual_inversion.py
|
||||||
|
'''
|
||||||
|
import math
|
||||||
|
import npyscreen
|
||||||
|
import curses
|
||||||
|
|
||||||
|
class FloatSlider(npyscreen.Slider):
|
||||||
|
# this is supposed to adjust display precision, but doesn't
|
||||||
|
def translate_value(self):
|
||||||
|
stri = "%3.2f / %3.2f" % (self.value, self.out_of)
|
||||||
|
l = (len(str(self.out_of))) * 2 + 4
|
||||||
|
stri = stri.rjust(l)
|
||||||
|
return stri
|
||||||
|
|
||||||
|
class FloatTitleSlider(npyscreen.TitleText):
|
||||||
|
_entry_type = FloatSlider
|
||||||
|
|
||||||
|
class MultiSelectColumns(npyscreen.MultiSelect):
|
||||||
|
def __init__(self, screen, columns: int=1, values: list=[], **keywords):
|
||||||
|
self.columns = columns
|
||||||
|
self.value_cnt = len(values)
|
||||||
|
self.rows = math.ceil(self.value_cnt / self.columns)
|
||||||
|
super().__init__(screen,values=values, **keywords)
|
||||||
|
|
||||||
|
def make_contained_widgets(self):
|
||||||
|
self._my_widgets = []
|
||||||
|
column_width = self.width // self.columns
|
||||||
|
for h in range(self.value_cnt):
|
||||||
|
self._my_widgets.append(
|
||||||
|
self._contained_widgets(self.parent,
|
||||||
|
rely=self.rely + (h % self.rows) * self._contained_widget_height,
|
||||||
|
relx=self.relx + (h // self.rows) * column_width,
|
||||||
|
max_width=column_width,
|
||||||
|
max_height=self.__class__._contained_widget_height,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
def set_up_handlers(self):
|
||||||
|
super().set_up_handlers()
|
||||||
|
self.handlers.update({
|
||||||
|
curses.KEY_UP: self.h_cursor_line_left,
|
||||||
|
curses.KEY_DOWN: self.h_cursor_line_right,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
def h_cursor_line_down(self, ch):
|
||||||
|
self.cursor_line += self.rows
|
||||||
|
if self.cursor_line >= len(self.values):
|
||||||
|
if self.scroll_exit:
|
||||||
|
self.cursor_line = len(self.values)-self.rows
|
||||||
|
self.h_exit_down(ch)
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
self.cursor_line -= self.rows
|
||||||
|
return True
|
||||||
|
|
||||||
|
def h_cursor_line_up(self, ch):
|
||||||
|
self.cursor_line -= self.rows
|
||||||
|
if self.cursor_line < 0:
|
||||||
|
if self.scroll_exit:
|
||||||
|
self.cursor_line = 0
|
||||||
|
self.h_exit_up(ch)
|
||||||
|
else:
|
||||||
|
self.cursor_line = 0
|
||||||
|
|
||||||
|
def h_cursor_line_left(self,ch):
|
||||||
|
super().h_cursor_line_up(ch)
|
||||||
|
|
||||||
|
def h_cursor_line_right(self,ch):
|
||||||
|
super().h_cursor_line_down(ch)
|
@ -20,6 +20,7 @@ from diffusers import logging as dlogging
|
|||||||
from npyscreen import widget
|
from npyscreen import widget
|
||||||
from omegaconf import OmegaConf
|
from omegaconf import OmegaConf
|
||||||
|
|
||||||
|
from ldm.invoke.config.widgets import FloatTitleSlider
|
||||||
from ldm.invoke.globals import (Globals, global_cache_dir, global_config_file,
|
from ldm.invoke.globals import (Globals, global_cache_dir, global_config_file,
|
||||||
global_models_dir, global_set_root)
|
global_models_dir, global_set_root)
|
||||||
from ldm.invoke.model_manager import ModelManager
|
from ldm.invoke.model_manager import ModelManager
|
||||||
@ -172,17 +173,6 @@ def _parse_args() -> Namespace:
|
|||||||
|
|
||||||
|
|
||||||
# ------------------------- GUI HERE -------------------------
|
# ------------------------- GUI HERE -------------------------
|
||||||
class FloatSlider(npyscreen.Slider):
|
|
||||||
# this is supposed to adjust display precision, but doesn't
|
|
||||||
def translate_value(self):
|
|
||||||
stri = "%3.2f / %3.2f" % (self.value, self.out_of)
|
|
||||||
l = (len(str(self.out_of))) * 2 + 4
|
|
||||||
stri = stri.rjust(l)
|
|
||||||
return stri
|
|
||||||
|
|
||||||
class FloatTitleSlider(npyscreen.TitleText):
|
|
||||||
_entry_type = FloatSlider
|
|
||||||
|
|
||||||
class mergeModelsForm(npyscreen.FormMultiPageAction):
|
class mergeModelsForm(npyscreen.FormMultiPageAction):
|
||||||
interpolations = ["weighted_sum", "sigmoid", "inv_sigmoid", "add_difference"]
|
interpolations = ["weighted_sum", "sigmoid", "inv_sigmoid", "add_difference"]
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user