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 # 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__":

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 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"]