convert existing model display to column format

This commit is contained in:
Lincoln Stein 2023-02-14 16:32:54 -05:00
parent 7545e38655
commit f299f40763
3 changed files with 186 additions and 89 deletions

View File

@ -7,6 +7,7 @@
# Coauthor: Kevin Turner http://github.com/keturn
#
import argparse
import curses
import os
import re
import shutil
@ -22,6 +23,7 @@ import npyscreen
import requests
from diffusers import AutoencoderKL
from huggingface_hub import hf_hub_url
from npyscreen import widget
from omegaconf import OmegaConf
from omegaconf.dictconfig import DictConfig
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.generator.diffusers_pipeline import StableDiffusionGeneratorPipeline
from ldm.invoke.globals import Globals, global_cache_dir, global_config_dir
from ldm.invoke.config.widgets import MultiSelectColumns
warnings.filterwarnings("ignore")
import torch
@ -84,37 +87,52 @@ class addModelsForm(npyscreen.FormMultiPageAction):
self.starter_model_list = [
x for x in list(self.initial_models.keys()) if x not in self.existing_models
]
self.installed_models=dict()
super().__init__(parentApp, name)
def create(self):
starter_model_labels = [
"%-30s %-50s" % (x, self.initial_models[x].description)
for x in self.starter_model_list
]
window_height, window_width = curses.initscr().getmaxyx()
starter_model_labels = self._get_starter_model_labels()
recommended_models = [
x
for x in self.starter_model_list
if self.initial_models[x].get("recommended", False)
]
previously_installed_models = [
x for x in list(self.initial_models.keys()) if x in self.existing_models
]
previously_installed_models = sorted(
[
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(
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,
color="CONTROL",
)
self.add_widget_intelligent(
npyscreen.FixedText,
value="Select models to install:",
editable=False,
color="LABELBOLD",
)
self.nextrely -= 2
self.add_widget_intelligent(npyscreen.FixedText, value="", editable=False),
self.models_selected = self.add_widget_intelligent(
npyscreen.MultiSelect,
name="Install/Remove Models",
name="Install Starter Models",
values=starter_model_labels,
value=[
self.starter_model_list.index(x)
@ -124,72 +142,80 @@ class addModelsForm(npyscreen.FormMultiPageAction):
max_height=len(starter_model_labels) + 1,
scroll_exit=True,
)
if len(previously_installed_models) > 0:
title = self.add_widget_intelligent(
npyscreen.TitleText,
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
for line in [
'Import checkpoint/safetensor models from the directory below.',
'(Use <tab> to autocomplete)'
]:
self.add_widget_intelligent(
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,
color="CONTROL",
)
self.model_names = self.add_widget_intelligent(
npyscreen.MultiLineEdit,
max_width=75,
max_height=16,
scroll_exit=True,
relx=18
)
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.nextrely -= 1
self.autoload_directory = self.add_widget_intelligent(
npyscreen.TitleFilename,
name='Directory:',
select_dir=True,
must_exist=True,
use_two_lines=False,
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
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):
self.parentApp.setNextForm(None)
@ -607,12 +633,23 @@ def main():
try:
select_and_download_models(opt)
except AssertionError as e:
print(str(e))
sys.exit(-1)
except KeyboardInterrupt:
print("\nGoodbye! Come back soon.")
except Exception as e:
print(f'\nA problem occurred during initialization.\nThe error was: "{str(e)}"')
print(traceback.format_exc())
except (widget.NotEnoughSpaceForWidget, Exception) as e:
if str(e).startswith("Height of 1 allocated"):
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__":

View 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)

View File

@ -20,6 +20,7 @@ from diffusers import logging as dlogging
from npyscreen import widget
from omegaconf import OmegaConf
from ldm.invoke.config.widgets import FloatTitleSlider
from ldm.invoke.globals import (Globals, global_cache_dir, global_config_file,
global_models_dir, global_set_root)
from ldm.invoke.model_manager import ModelManager
@ -172,17 +173,6 @@ def _parse_args() -> Namespace:
# ------------------------- 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):
interpolations = ["weighted_sum", "sigmoid", "inv_sigmoid", "add_difference"]