mirror of
https://github.com/invoke-ai/InvokeAI
synced 2024-08-30 20:32:17 +00:00
installer enhancements
- Detect when install failed for some reason and print helpful error message rather than stack trace. - Detect window size and resize to minimum acceptable values to provide better display of configure and install forms.
This commit is contained in:
parent
8b6196e0a2
commit
3083356cf0
@ -346,7 +346,21 @@ class InvokeAiInstance:
|
|||||||
# NOTE: currently the config script does its own arg parsing! this means the command-line switches
|
# NOTE: currently the config script does its own arg parsing! this means the command-line switches
|
||||||
# from the installer will also automatically propagate down to the config script.
|
# from the installer will also automatically propagate down to the config script.
|
||||||
# this may change in the future with config refactoring!
|
# this may change in the future with config refactoring!
|
||||||
invokeai_configure.main()
|
succeeded = False
|
||||||
|
try:
|
||||||
|
invokeai_configure.main()
|
||||||
|
succeeded = True
|
||||||
|
except ConnectionError as e:
|
||||||
|
print(f'A network error was encountered during configuration and download: {str(e)}')
|
||||||
|
except OSError as e:
|
||||||
|
print(f'An OS error was encountered during configuration and download: {str(e)}')
|
||||||
|
except Exception as e:
|
||||||
|
print(f'A problem was encountered during the configuration and download steps: {str(e)}')
|
||||||
|
finally:
|
||||||
|
if not succeeded:
|
||||||
|
print('You may be able to finish the process by launching "invoke.sh" or "invoke.bat"')
|
||||||
|
print('from within the "invokeai" directory, and choosing options 5 and/or 6.')
|
||||||
|
print('Alternatively you can relaunch the installer.')
|
||||||
|
|
||||||
def install_user_scripts(self):
|
def install_user_scripts(self):
|
||||||
"""
|
"""
|
||||||
|
@ -8,7 +8,6 @@
|
|||||||
#
|
#
|
||||||
print("Loading Python libraries...\n")
|
print("Loading Python libraries...\n")
|
||||||
import argparse
|
import argparse
|
||||||
import curses
|
|
||||||
import io
|
import io
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
@ -19,6 +18,7 @@ import warnings
|
|||||||
from argparse import Namespace
|
from argparse import Namespace
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from urllib import request
|
from urllib import request
|
||||||
|
from shutil import get_terminal_size
|
||||||
|
|
||||||
import npyscreen
|
import npyscreen
|
||||||
import torch
|
import torch
|
||||||
@ -46,7 +46,8 @@ from .model_install_backend import (
|
|||||||
recommended_datasets,
|
recommended_datasets,
|
||||||
hf_download_with_resume,
|
hf_download_with_resume,
|
||||||
)
|
)
|
||||||
from .widgets import IntTitleSlider, CenteredButtonPress
|
from .widgets import IntTitleSlider, CenteredButtonPress, set_min_terminal_size
|
||||||
|
|
||||||
|
|
||||||
warnings.filterwarnings("ignore")
|
warnings.filterwarnings("ignore")
|
||||||
|
|
||||||
@ -64,6 +65,10 @@ SD_Configs = Path(global_config_dir()) / "stable-diffusion"
|
|||||||
|
|
||||||
Datasets = OmegaConf.load(Dataset_path)
|
Datasets = OmegaConf.load(Dataset_path)
|
||||||
|
|
||||||
|
# minimum size for the UI
|
||||||
|
MIN_COLS = 120
|
||||||
|
MIN_LINES = 45
|
||||||
|
|
||||||
INIT_FILE_PREAMBLE = """# InvokeAI initialization file
|
INIT_FILE_PREAMBLE = """# InvokeAI initialization file
|
||||||
# This is the InvokeAI initialization file, which contains command-line default values.
|
# This is the InvokeAI initialization file, which contains command-line default values.
|
||||||
# Feel free to edit. If anything goes wrong, you can re-initialize this file by deleting
|
# Feel free to edit. If anything goes wrong, you can re-initialize this file by deleting
|
||||||
@ -109,8 +114,6 @@ Add the '--help' argument to see all of the command-line switches available for
|
|||||||
|
|
||||||
# ---------------------------------------------
|
# ---------------------------------------------
|
||||||
def yes_or_no(prompt: str, default_yes=True):
|
def yes_or_no(prompt: str, default_yes=True):
|
||||||
completer.set_options(["yes", "no"])
|
|
||||||
completer.complete_extensions(None) # turn off path-completion mode
|
|
||||||
default = "y" if default_yes else "n"
|
default = "y" if default_yes else "n"
|
||||||
response = input(f"{prompt} [{default}] ") or default
|
response = input(f"{prompt} [{default}] ") or default
|
||||||
if default_yes:
|
if default_yes:
|
||||||
@ -332,8 +335,7 @@ class editOptsForm(npyscreen.FormMultiPage):
|
|||||||
old_opts = self.parentApp.invokeai_opts
|
old_opts = self.parentApp.invokeai_opts
|
||||||
first_time = not (Globals.root / Globals.initfile).exists()
|
first_time = not (Globals.root / Globals.initfile).exists()
|
||||||
access_token = HfFolder.get_token()
|
access_token = HfFolder.get_token()
|
||||||
|
window_width,window_height = get_terminal_size()
|
||||||
window_height, window_width = curses.initscr().getmaxyx()
|
|
||||||
for i in [
|
for i in [
|
||||||
"Configure startup settings. You can come back and change these later.",
|
"Configure startup settings. You can come back and change these later.",
|
||||||
"Use ctrl-N and ctrl-P to move to the <N>ext and <P>revious fields.",
|
"Use ctrl-N and ctrl-P to move to the <N>ext and <P>revious fields.",
|
||||||
@ -676,6 +678,8 @@ def run_console_ui(
|
|||||||
) -> (Namespace, Namespace):
|
) -> (Namespace, Namespace):
|
||||||
# parse_args() will read from init file if present
|
# parse_args() will read from init file if present
|
||||||
invokeai_opts = default_startup_options(initfile)
|
invokeai_opts = default_startup_options(initfile)
|
||||||
|
|
||||||
|
set_min_terminal_size(MIN_COLS, MIN_LINES)
|
||||||
editApp = EditOptApplication(program_opts, invokeai_opts)
|
editApp = EditOptApplication(program_opts, invokeai_opts)
|
||||||
editApp.run()
|
editApp.run()
|
||||||
if editApp.user_cancelled:
|
if editApp.user_cancelled:
|
||||||
@ -683,7 +687,6 @@ def run_console_ui(
|
|||||||
else:
|
else:
|
||||||
return (editApp.new_opts, editApp.user_selections)
|
return (editApp.new_opts, editApp.user_selections)
|
||||||
|
|
||||||
|
|
||||||
# -------------------------------------
|
# -------------------------------------
|
||||||
def write_opts(opts: Namespace, init_file: Path):
|
def write_opts(opts: Namespace, init_file: Path):
|
||||||
"""
|
"""
|
||||||
|
@ -10,7 +10,6 @@ The work is actually done in backend code in model_install_backend.py.
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
import argparse
|
import argparse
|
||||||
import curses
|
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
import traceback
|
import traceback
|
||||||
@ -22,15 +21,23 @@ import npyscreen
|
|||||||
import torch
|
import torch
|
||||||
from npyscreen import widget
|
from npyscreen import widget
|
||||||
from omegaconf import OmegaConf
|
from omegaconf import OmegaConf
|
||||||
|
from shutil import get_terminal_size
|
||||||
|
|
||||||
from ..devices import choose_precision, choose_torch_device
|
from ..devices import choose_precision, choose_torch_device
|
||||||
from ..globals import Globals, global_config_dir
|
from ..globals import Globals, global_config_dir
|
||||||
from .model_install_backend import (Dataset_path, default_config_file,
|
from .model_install_backend import (Dataset_path, default_config_file,
|
||||||
default_dataset, get_root,
|
default_dataset, get_root,
|
||||||
install_requested_models,
|
install_requested_models,
|
||||||
recommended_datasets)
|
recommended_datasets,
|
||||||
|
)
|
||||||
from .widgets import (MultiSelectColumns, TextBox,
|
from .widgets import (MultiSelectColumns, TextBox,
|
||||||
OffsetButtonPress, CenteredTitleText)
|
OffsetButtonPress, CenteredTitleText,
|
||||||
|
set_min_terminal_size,
|
||||||
|
)
|
||||||
|
|
||||||
|
# minimum size for the UI
|
||||||
|
MIN_COLS = 120
|
||||||
|
MIN_LINES = 45
|
||||||
|
|
||||||
class addModelsForm(npyscreen.FormMultiPage):
|
class addModelsForm(npyscreen.FormMultiPage):
|
||||||
# for responsive resizing - disabled
|
# for responsive resizing - disabled
|
||||||
@ -50,7 +57,7 @@ class addModelsForm(npyscreen.FormMultiPage):
|
|||||||
super().__init__(parentApp=parentApp, name=name, *args, **keywords)
|
super().__init__(parentApp=parentApp, name=name, *args, **keywords)
|
||||||
|
|
||||||
def create(self):
|
def create(self):
|
||||||
window_height, window_width = curses.initscr().getmaxyx()
|
window_width, window_height = get_terminal_size()
|
||||||
starter_model_labels = self._get_starter_model_labels()
|
starter_model_labels = self._get_starter_model_labels()
|
||||||
recommended_models = [
|
recommended_models = [
|
||||||
x
|
x
|
||||||
@ -249,7 +256,7 @@ class addModelsForm(npyscreen.FormMultiPage):
|
|||||||
)
|
)
|
||||||
|
|
||||||
def _get_starter_model_labels(self) -> List[str]:
|
def _get_starter_model_labels(self) -> List[str]:
|
||||||
window_height, window_width = curses.initscr().getmaxyx()
|
window_width, window_height = get_terminal_size()
|
||||||
label_width = 25
|
label_width = 25
|
||||||
checkbox_width = 4
|
checkbox_width = 4
|
||||||
spacing_width = 2
|
spacing_width = 2
|
||||||
@ -268,7 +275,7 @@ class addModelsForm(npyscreen.FormMultiPage):
|
|||||||
]
|
]
|
||||||
|
|
||||||
def _get_columns(self) -> int:
|
def _get_columns(self) -> int:
|
||||||
window_height, window_width = curses.initscr().getmaxyx()
|
window_width, window_height = get_terminal_size()
|
||||||
cols = (
|
cols = (
|
||||||
4
|
4
|
||||||
if window_width > 240
|
if window_width > 240
|
||||||
@ -362,7 +369,6 @@ class AddModelApplication(npyscreen.NPSAppManaged):
|
|||||||
"MAIN", addModelsForm, name="Install Stable Diffusion Models"
|
"MAIN", addModelsForm, name="Install Stable Diffusion Models"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
# --------------------------------------------------------
|
# --------------------------------------------------------
|
||||||
def process_and_execute(opt: Namespace, selections: Namespace):
|
def process_and_execute(opt: Namespace, selections: Namespace):
|
||||||
models_to_remove = [
|
models_to_remove = [
|
||||||
@ -409,6 +415,7 @@ def select_and_download_models(opt: Namespace):
|
|||||||
precision=precision,
|
precision=precision,
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
|
set_min_terminal_size(MIN_COLS, MIN_LINES)
|
||||||
installApp = AddModelApplication()
|
installApp = AddModelApplication()
|
||||||
installApp.run()
|
installApp.run()
|
||||||
|
|
||||||
|
@ -67,6 +67,9 @@ def install_requested_models(
|
|||||||
purge_deleted: bool = False,
|
purge_deleted: bool = False,
|
||||||
config_file_path: Path = None,
|
config_file_path: Path = None,
|
||||||
):
|
):
|
||||||
|
'''
|
||||||
|
Entry point for installing/deleting starter models, or installing external models.
|
||||||
|
'''
|
||||||
config_file_path=config_file_path or default_config_file()
|
config_file_path=config_file_path or default_config_file()
|
||||||
if not config_file_path.exists():
|
if not config_file_path.exists():
|
||||||
open(config_file_path,'w')
|
open(config_file_path,'w')
|
||||||
|
@ -2,8 +2,34 @@
|
|||||||
Widget class definitions used by model_select.py, merge_diffusers.py and textual_inversion.py
|
Widget class definitions used by model_select.py, merge_diffusers.py and textual_inversion.py
|
||||||
'''
|
'''
|
||||||
import math
|
import math
|
||||||
|
import platform
|
||||||
import npyscreen
|
import npyscreen
|
||||||
|
import sys
|
||||||
import curses
|
import curses
|
||||||
|
import termios
|
||||||
|
import struct
|
||||||
|
import fcntl
|
||||||
|
|
||||||
|
from shutil import get_terminal_size
|
||||||
|
|
||||||
|
# -------------------------------------
|
||||||
|
def set_terminal_size(columns: int, lines: int):
|
||||||
|
os = platform.uname().system
|
||||||
|
if os=="Windows":
|
||||||
|
os.system(f'mode con: cols={columns} lines={lines}')
|
||||||
|
elif os in ['Darwin', 'Linux']:
|
||||||
|
winsize = struct.pack("HHHH", lines, columns, 0, 0)
|
||||||
|
fcntl.ioctl(0, termios.TIOCSWINSZ, winsize)
|
||||||
|
sys.stdout.write("\x1b[8;{rows};{cols}t".format(rows=lines, cols=columns))
|
||||||
|
sys.stdout.flush()
|
||||||
|
|
||||||
|
|
||||||
|
def set_min_terminal_size(min_cols: int, min_lines: int):
|
||||||
|
# make sure there's enough room for the ui
|
||||||
|
term_cols, term_lines = get_terminal_size()
|
||||||
|
cols = max(term_cols, min_cols)
|
||||||
|
lines = max(term_lines, min_lines)
|
||||||
|
set_terminal_size(cols,lines)
|
||||||
|
|
||||||
class IntSlider(npyscreen.Slider):
|
class IntSlider(npyscreen.Slider):
|
||||||
def translate_value(self):
|
def translate_value(self):
|
||||||
@ -23,7 +49,6 @@ class CenteredTitleText(npyscreen.TitleText):
|
|||||||
maxy, maxx = self.parent.curses_pad.getmaxyx()
|
maxy, maxx = self.parent.curses_pad.getmaxyx()
|
||||||
label = self.name
|
label = self.name
|
||||||
self.relx = (maxx - len(label)) // 2
|
self.relx = (maxx - len(label)) // 2
|
||||||
begin_entry_at = -self.relx + 2
|
|
||||||
|
|
||||||
# -------------------------------------
|
# -------------------------------------
|
||||||
class CenteredButtonPress(npyscreen.ButtonPress):
|
class CenteredButtonPress(npyscreen.ButtonPress):
|
||||||
|
Loading…
Reference in New Issue
Block a user