mirror of
https://github.com/invoke-ai/InvokeAI
synced 2024-08-30 20:32:17 +00:00
multiple small fixes
1. Contents of autoscan directory field are restored after doing an installation. 2. Activate dialogue to choose V2 parameterization when importing from a directory. 3. Remove autoscan directory from init file when its checkbox is unselected. 4. Add widget cycling behavior to install models form.
This commit is contained in:
parent
a3357e073c
commit
9ed86a08f1
@ -13,6 +13,7 @@ import argparse
|
|||||||
import io
|
import io
|
||||||
import os
|
import os
|
||||||
import shutil
|
import shutil
|
||||||
|
import textwrap
|
||||||
import traceback
|
import traceback
|
||||||
import warnings
|
import warnings
|
||||||
from argparse import Namespace
|
from argparse import Namespace
|
||||||
@ -321,11 +322,11 @@ class editOptsForm(CyclingForm, npyscreen.FormMultiPage):
|
|||||||
first_time = not (config.root_path / 'invokeai.yaml').exists()
|
first_time = not (config.root_path / 'invokeai.yaml').exists()
|
||||||
access_token = HfFolder.get_token()
|
access_token = HfFolder.get_token()
|
||||||
window_width, window_height = get_terminal_size()
|
window_width, window_height = get_terminal_size()
|
||||||
for i in [
|
label = """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.",
|
Use cursor arrows to make a checkbox selection, and space to toggle.
|
||||||
"Use cursor arrows to make a checkbox selection, and space to toggle.",
|
"""
|
||||||
]:
|
for i in textwrap.wrap(label,width=window_width-6):
|
||||||
self.add_widget_intelligent(
|
self.add_widget_intelligent(
|
||||||
npyscreen.FixedText,
|
npyscreen.FixedText,
|
||||||
value=i,
|
value=i,
|
||||||
@ -375,14 +376,13 @@ class editOptsForm(CyclingForm, npyscreen.FormMultiPage):
|
|||||||
scroll_exit=True,
|
scroll_exit=True,
|
||||||
)
|
)
|
||||||
self.nextrely += 1
|
self.nextrely += 1
|
||||||
for i in [
|
label = """If you have an account at HuggingFace you may optionally paste your access token here
|
||||||
"If you have an account at HuggingFace you may optionally paste your access token here",
|
to allow InvokeAI to download restricted styles & subjects from the "Concept Library". See https://huggingface.co/settings/tokens.
|
||||||
'to allow InvokeAI to download restricted styles & subjects from the "Concept Library".',
|
"""
|
||||||
"See https://huggingface.co/settings/tokens",
|
for line in textwrap.wrap(label,width=window_width-6):
|
||||||
]:
|
|
||||||
self.add_widget_intelligent(
|
self.add_widget_intelligent(
|
||||||
npyscreen.FixedText,
|
npyscreen.FixedText,
|
||||||
value=i,
|
value=line,
|
||||||
editable=False,
|
editable=False,
|
||||||
color="CONTROL",
|
color="CONTROL",
|
||||||
)
|
)
|
||||||
@ -506,11 +506,11 @@ class editOptsForm(CyclingForm, npyscreen.FormMultiPage):
|
|||||||
scroll_exit=True,
|
scroll_exit=True,
|
||||||
)
|
)
|
||||||
self.nextrely -= 1
|
self.nextrely -= 1
|
||||||
for i in [
|
label = """BY DOWNLOADING THE STABLE DIFFUSION WEIGHT FILES, YOU AGREE TO HAVE READ
|
||||||
"BY DOWNLOADING THE STABLE DIFFUSION WEIGHT FILES, YOU AGREE TO HAVE READ",
|
AND ACCEPTED THE CREATIVEML RESPONSIBLE AI LICENSE LOCATED AT
|
||||||
"AND ACCEPTED THE CREATIVEML RESPONSIBLE AI LICENSE LOCATED AT",
|
https://huggingface.co/spaces/CompVis/stable-diffusion-license
|
||||||
"https://huggingface.co/spaces/CompVis/stable-diffusion-license",
|
"""
|
||||||
]:
|
for i in textwrap.wrap(label,width=window_width-6):
|
||||||
self.add_widget_intelligent(
|
self.add_widget_intelligent(
|
||||||
npyscreen.FixedText,
|
npyscreen.FixedText,
|
||||||
value=i,
|
value=i,
|
||||||
@ -621,6 +621,7 @@ class EditOptApplication(npyscreen.NPSAppManaged):
|
|||||||
addModelsForm,
|
addModelsForm,
|
||||||
name="Install Stable Diffusion Models",
|
name="Install Stable Diffusion Models",
|
||||||
multipage=True,
|
multipage=True,
|
||||||
|
cycle_widgets=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
def new_opts(self):
|
def new_opts(self):
|
||||||
|
@ -157,6 +157,7 @@ def install_requested_models(
|
|||||||
logger.info("INSTALLING EXTERNAL MODELS")
|
logger.info("INSTALLING EXTERNAL MODELS")
|
||||||
for path_url_or_repo in external_models:
|
for path_url_or_repo in external_models:
|
||||||
try:
|
try:
|
||||||
|
logger.debug(f'In install_requested_models; callback = {model_config_file_callback}')
|
||||||
model_manager.heuristic_import(
|
model_manager.heuristic_import(
|
||||||
path_url_or_repo,
|
path_url_or_repo,
|
||||||
commit_to_conf=config_file_path,
|
commit_to_conf=config_file_path,
|
||||||
@ -169,6 +170,8 @@ def install_requested_models(
|
|||||||
|
|
||||||
if scan_at_startup and scan_directory.is_dir():
|
if scan_at_startup and scan_directory.is_dir():
|
||||||
update_autoconvert_dir(scan_directory)
|
update_autoconvert_dir(scan_directory)
|
||||||
|
else:
|
||||||
|
update_autoconvert_dir(None)
|
||||||
|
|
||||||
def update_autoconvert_dir(autodir: Path):
|
def update_autoconvert_dir(autodir: Path):
|
||||||
'''
|
'''
|
||||||
@ -176,7 +179,7 @@ def update_autoconvert_dir(autodir: Path):
|
|||||||
'''
|
'''
|
||||||
invokeai_config_path = config.init_file_path
|
invokeai_config_path = config.init_file_path
|
||||||
conf = OmegaConf.load(invokeai_config_path)
|
conf = OmegaConf.load(invokeai_config_path)
|
||||||
conf.InvokeAI.Paths.autoconvert_dir = str(autodir)
|
conf.InvokeAI.Paths.autoconvert_dir = str(autodir) if autodir else None
|
||||||
yaml = OmegaConf.to_yaml(conf)
|
yaml = OmegaConf.to_yaml(conf)
|
||||||
tmpfile = invokeai_config_path.parent / "new_config.tmp"
|
tmpfile = invokeai_config_path.parent / "new_config.tmp"
|
||||||
with open(tmpfile, "w", encoding="utf-8") as outfile:
|
with open(tmpfile, "w", encoding="utf-8") as outfile:
|
||||||
|
@ -321,6 +321,10 @@ class ModelManager(object):
|
|||||||
for name in sorted(self.config, key=str.casefold):
|
for name in sorted(self.config, key=str.casefold):
|
||||||
stanza = self.config[name]
|
stanza = self.config[name]
|
||||||
|
|
||||||
|
with open('log.txt','a') as file:
|
||||||
|
print(f'DEBUG: name={name}; stanza = {stanza}',file=file)
|
||||||
|
|
||||||
|
|
||||||
# don't include VAEs in listing (legacy style)
|
# don't include VAEs in listing (legacy style)
|
||||||
if "config" in stanza and "/VAE/" in stanza["config"]:
|
if "config" in stanza and "/VAE/" in stanza["config"]:
|
||||||
continue
|
continue
|
||||||
@ -820,7 +824,9 @@ class ModelManager(object):
|
|||||||
Path(thing).rglob("*.safetensors")
|
Path(thing).rglob("*.safetensors")
|
||||||
):
|
):
|
||||||
if model_name := self.heuristic_import(
|
if model_name := self.heuristic_import(
|
||||||
str(m), commit_to_conf=commit_to_conf
|
str(m),
|
||||||
|
commit_to_conf=commit_to_conf,
|
||||||
|
config_file_callback=config_file_callback,
|
||||||
):
|
):
|
||||||
self.logger.info(f"{model_name} successfully imported")
|
self.logger.info(f"{model_name} successfully imported")
|
||||||
return model_name
|
return model_name
|
||||||
|
@ -14,6 +14,7 @@ import curses
|
|||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
import textwrap
|
import textwrap
|
||||||
|
import traceback
|
||||||
from argparse import Namespace
|
from argparse import Namespace
|
||||||
from multiprocessing import Process
|
from multiprocessing import Process
|
||||||
from multiprocessing.connection import Connection, Pipe
|
from multiprocessing.connection import Connection, Pipe
|
||||||
@ -128,10 +129,10 @@ class addModelsForm(CyclingForm, npyscreen.FormMultiPage):
|
|||||||
SingleSelectColumns,
|
SingleSelectColumns,
|
||||||
values=[
|
values=[
|
||||||
'STARTER MODELS',
|
'STARTER MODELS',
|
||||||
'MORE DIFFUSION MODELS',
|
'MORE MODELS',
|
||||||
'CONTROLNET MODELS',
|
'CONTROLNETS',
|
||||||
'LORA/LYCORIS MODELS',
|
'LORA/LYCORIS',
|
||||||
'TEXTUAL INVERSION MODELS',
|
'TEXTUAL INVERSION',
|
||||||
],
|
],
|
||||||
value=[self.current_tab],
|
value=[self.current_tab],
|
||||||
columns = 5,
|
columns = 5,
|
||||||
@ -183,33 +184,28 @@ class addModelsForm(CyclingForm, npyscreen.FormMultiPage):
|
|||||||
BufferBox,
|
BufferBox,
|
||||||
name='Log Messages',
|
name='Log Messages',
|
||||||
editable=False,
|
editable=False,
|
||||||
max_height = 20,
|
max_height = 16,
|
||||||
)
|
)
|
||||||
|
|
||||||
self.nextrely += 1
|
self.nextrely += 1
|
||||||
done_label = "INSTALL/REMOVE NOW"
|
done_label = "APPLY CHANGES"
|
||||||
back_label = "BACK"
|
back_label = "BACK"
|
||||||
button_length = len(done_label)
|
|
||||||
button_offset = 0
|
|
||||||
if self.multipage:
|
if self.multipage:
|
||||||
button_length += len(back_label) + 1
|
|
||||||
button_offset += len(back_label) + 1
|
|
||||||
self.back_button = self.add_widget_intelligent(
|
self.back_button = self.add_widget_intelligent(
|
||||||
npyscreen.ButtonPress,
|
npyscreen.ButtonPress,
|
||||||
name=back_label,
|
name=back_label,
|
||||||
relx=(window_width - button_length) // 2,
|
|
||||||
rely=-3,
|
rely=-3,
|
||||||
when_pressed_function=self.on_back,
|
when_pressed_function=self.on_back,
|
||||||
)
|
)
|
||||||
self.ok_button = self.add_widget_intelligent(
|
self.ok_button = self.add_widget_intelligent(
|
||||||
npyscreen.ButtonPress, # OffsetButtonPress,
|
npyscreen.ButtonPress,
|
||||||
name=done_label,
|
name=done_label,
|
||||||
relx=button_offset + 1 + (window_width - button_length) // 2,
|
relx=(window_width - len(done_label)) // 2,
|
||||||
rely=-3,
|
rely=-3,
|
||||||
when_pressed_function=self.on_execute
|
when_pressed_function=self.on_execute
|
||||||
)
|
)
|
||||||
|
|
||||||
label = "INSTALL AND EXIT"
|
label = "APPLY CHANGES & EXIT"
|
||||||
self.done = self.add_widget_intelligent(
|
self.done = self.add_widget_intelligent(
|
||||||
npyscreen.ButtonPress,
|
npyscreen.ButtonPress,
|
||||||
name=label,
|
name=label,
|
||||||
@ -289,13 +285,14 @@ class addModelsForm(CyclingForm, npyscreen.FormMultiPage):
|
|||||||
model_type: str,
|
model_type: str,
|
||||||
window_width: int=120,
|
window_width: int=120,
|
||||||
install_prompt: str=None,
|
install_prompt: str=None,
|
||||||
|
add_purge_deleted: bool=False,
|
||||||
)->dict[str,npyscreen.widget]:
|
)->dict[str,npyscreen.widget]:
|
||||||
'''Generic code to create model selection widgets'''
|
'''Generic code to create model selection widgets'''
|
||||||
widgets = dict()
|
widgets = dict()
|
||||||
model_list = sorted(predefined_models.keys())
|
model_list = sorted(predefined_models.keys())
|
||||||
if len(model_list) > 0:
|
if len(model_list) > 0:
|
||||||
max_width = max([len(x) for x in model_list])
|
max_width = max([len(x) for x in model_list])
|
||||||
columns = window_width // (max_width+6) # 6 characters for "[x] " and padding
|
columns = window_width // (max_width+8) # 8 characters for "[x] " and padding
|
||||||
columns = min(len(model_list),columns) or 1
|
columns = min(len(model_list),columns) or 1
|
||||||
prompt = install_prompt or f"Select the desired {model_type} models to install. Unchecked models will be purged from disk."
|
prompt = install_prompt or f"Select the desired {model_type} models to install. Unchecked models will be purged from disk."
|
||||||
|
|
||||||
@ -325,26 +322,27 @@ class addModelsForm(CyclingForm, npyscreen.FormMultiPage):
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
self.nextrely += 1
|
if add_purge_deleted:
|
||||||
widgets.update(
|
self.nextrely += 1
|
||||||
label2 = self.add_widget_intelligent(
|
widgets.update(
|
||||||
npyscreen.TitleFixedText,
|
purge_deleted = self.add_widget_intelligent(
|
||||||
name="Additional URLs or HuggingFace repo_ids to install (Space separated. Use shift-control-V to paste):",
|
npyscreen.Checkbox,
|
||||||
relx=4,
|
name="Purge unchecked diffusers models from disk",
|
||||||
color='CONTROL',
|
value=False,
|
||||||
editable=False,
|
scroll_exit=True,
|
||||||
scroll_exit=True
|
relx=4,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
widgets['purge_deleted'].when_value_edited = lambda: self.sync_purge_buttons(widgets['purge_deleted'])
|
||||||
|
|
||||||
self.nextrely -= 1
|
self.nextrely += 1
|
||||||
widgets.update(
|
widgets.update(
|
||||||
download_ids = self.add_widget_intelligent(
|
download_ids = self.add_widget_intelligent(
|
||||||
TextBox,
|
TextBox,
|
||||||
|
name = "Additional URLs, or HuggingFace repo_ids to install (Space separated. Use shift-control-V to paste):",
|
||||||
max_height=4,
|
max_height=4,
|
||||||
scroll_exit=True,
|
scroll_exit=True,
|
||||||
editable=True,
|
editable=True,
|
||||||
relx=4
|
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
return widgets
|
return widgets
|
||||||
@ -361,27 +359,18 @@ class addModelsForm(CyclingForm, npyscreen.FormMultiPage):
|
|||||||
predefined_models,
|
predefined_models,
|
||||||
'Diffusers',
|
'Diffusers',
|
||||||
window_width,
|
window_width,
|
||||||
install_prompt="Additional diffusers models already installed. Uncheck to purge from disk.",
|
install_prompt="Additional diffusers models already installed.",
|
||||||
|
add_purge_deleted=True
|
||||||
)
|
)
|
||||||
|
|
||||||
self.nextrely += 2
|
|
||||||
widgets.update(
|
|
||||||
purge_deleted = self.add_widget_intelligent(
|
|
||||||
npyscreen.Checkbox,
|
|
||||||
name="Purge unchecked diffusers models from disk",
|
|
||||||
value=False,
|
|
||||||
scroll_exit=True,
|
|
||||||
relx=4,
|
|
||||||
)
|
|
||||||
)
|
|
||||||
label = "Directory to scan for models to automatically import (<tab> autocompletes):"
|
label = "Directory to scan for models to automatically import (<tab> autocompletes):"
|
||||||
self.nextrely += 2
|
self.nextrely += 1
|
||||||
widgets.update(
|
widgets.update(
|
||||||
autoload_directory = self.add_widget_intelligent(
|
autoload_directory = self.add_widget_intelligent(
|
||||||
# npyscreen.TitleFilename,
|
|
||||||
FileBox,
|
FileBox,
|
||||||
max_height=3,
|
max_height=3,
|
||||||
name=label,
|
name=label,
|
||||||
|
value=str(config.autoconvert_dir) if config.autoconvert_dir else None,
|
||||||
select_dir=True,
|
select_dir=True,
|
||||||
must_exist=True,
|
must_exist=True,
|
||||||
use_two_lines=False,
|
use_two_lines=False,
|
||||||
@ -394,12 +383,11 @@ class addModelsForm(CyclingForm, npyscreen.FormMultiPage):
|
|||||||
autoscan_on_startup = self.add_widget_intelligent(
|
autoscan_on_startup = self.add_widget_intelligent(
|
||||||
npyscreen.Checkbox,
|
npyscreen.Checkbox,
|
||||||
name="Scan and import from this directory each time InvokeAI starts",
|
name="Scan and import from this directory each time InvokeAI starts",
|
||||||
value=False,
|
value=config.autoconvert_dir is not None,
|
||||||
relx=4,
|
relx=4,
|
||||||
scroll_exit=True,
|
scroll_exit=True,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
widgets['purge_deleted'].when_value_edited = lambda: self.sync_purge_buttons(widgets['purge_deleted'])
|
|
||||||
return widgets
|
return widgets
|
||||||
|
|
||||||
def sync_purge_buttons(self,checkbox):
|
def sync_purge_buttons(self,checkbox):
|
||||||
@ -557,15 +545,21 @@ class addModelsForm(CyclingForm, npyscreen.FormMultiPage):
|
|||||||
self.subprocess_connection = None
|
self.subprocess_connection = None
|
||||||
self.monitor.entry_widget.buffer(['** Action Complete **'])
|
self.monitor.entry_widget.buffer(['** Action Complete **'])
|
||||||
self.display()
|
self.display()
|
||||||
# rebuild the form, saving log messages
|
|
||||||
|
# rebuild the form, saving and restoring some of the fields that need to be preserved.
|
||||||
saved_messages = self.monitor.entry_widget.values
|
saved_messages = self.monitor.entry_widget.values
|
||||||
multipage = self.multipage
|
autoload_dir = self.diffusers_models['autoload_directory'].value
|
||||||
|
autoscan = self.diffusers_models['autoscan_on_startup'].value
|
||||||
|
|
||||||
app.main_form = app.addForm(
|
app.main_form = app.addForm(
|
||||||
"MAIN", addModelsForm, name="Install Stable Diffusion Models", multipage=multipage,
|
"MAIN", addModelsForm, name="Install Stable Diffusion Models", multipage=self.multipage,
|
||||||
)
|
)
|
||||||
app.switchForm("MAIN")
|
app.switchForm("MAIN")
|
||||||
|
|
||||||
app.main_form.monitor.entry_widget.values = saved_messages
|
app.main_form.monitor.entry_widget.values = saved_messages
|
||||||
app.main_form.monitor.entry_widget.buffer([''],scroll_end=True)
|
app.main_form.monitor.entry_widget.buffer([''],scroll_end=True)
|
||||||
|
app.main_form.diffusers_models['autoload_directory'].value = autoload_dir
|
||||||
|
app.main_form.diffusers_models['autoscan_on_startup'].value = autoscan
|
||||||
|
|
||||||
###############################################################
|
###############################################################
|
||||||
|
|
||||||
@ -822,6 +816,7 @@ def select_and_download_models(opt: Namespace):
|
|||||||
|
|
||||||
if do_listings(opt):
|
if do_listings(opt):
|
||||||
pass
|
pass
|
||||||
|
# this processes command line additions/removals
|
||||||
elif opt.diffusers or opt.controlnets or opt.textual_inversions or opt.loras:
|
elif opt.diffusers or opt.controlnets or opt.textual_inversions or opt.loras:
|
||||||
action = 'remove_models' if opt.delete else 'install_models'
|
action = 'remove_models' if opt.delete else 'install_models'
|
||||||
diffusers_args = {'diffusers':ModelInstallList(remove_models=opt.diffusers or [])} \
|
diffusers_args = {'diffusers':ModelInstallList(remove_models=opt.diffusers or [])} \
|
||||||
@ -846,6 +841,8 @@ def select_and_download_models(opt: Namespace):
|
|||||||
diffusers=ModelInstallList(install_models=recommended_datasets()),
|
diffusers=ModelInstallList(install_models=recommended_datasets()),
|
||||||
precision=precision,
|
precision=precision,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# this is where the TUI is called
|
||||||
else:
|
else:
|
||||||
# needed because the torch library is loaded, even though we don't use it
|
# needed because the torch library is loaded, even though we don't use it
|
||||||
torch.multiprocessing.set_start_method("spawn")
|
torch.multiprocessing.set_start_method("spawn")
|
||||||
@ -856,12 +853,14 @@ def select_and_download_models(opt: Namespace):
|
|||||||
installApp = AddModelApplication(opt)
|
installApp = AddModelApplication(opt)
|
||||||
try:
|
try:
|
||||||
installApp.run()
|
installApp.run()
|
||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt as e:
|
||||||
form = installApp.main_form
|
if hasattr(installApp,'main_form'):
|
||||||
if form.subprocess and form.subprocess.is_alive():
|
if installApp.main_form.subprocess \
|
||||||
logger.info('Terminating subprocesses')
|
and installApp.main_form.subprocess.is_alive():
|
||||||
form.subprocess.terminate()
|
logger.info('Terminating subprocesses')
|
||||||
form.subprocess = None
|
installApp.main_form.subprocess.terminate()
|
||||||
|
installApp.main_form.subprocess = None
|
||||||
|
raise e
|
||||||
process_and_execute(opt, installApp.user_selections)
|
process_and_execute(opt, installApp.user_selections)
|
||||||
|
|
||||||
# -------------------------------------
|
# -------------------------------------
|
||||||
@ -970,7 +969,8 @@ def main():
|
|||||||
"Insufficient horizontal space for the interface. Please make your window wider and try again."
|
"Insufficient horizontal space for the interface. Please make your window wider and try again."
|
||||||
)
|
)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f'An exception has occurred: {str(e)}')
|
print(f'An exception has occurred: {str(e)} Details:')
|
||||||
|
print(traceback.format_exc(), file=sys.stderr)
|
||||||
input('Press any key to continue...')
|
input('Press any key to continue...')
|
||||||
|
|
||||||
|
|
||||||
|
@ -17,7 +17,7 @@ from shutil import get_terminal_size
|
|||||||
from curses import BUTTON2_CLICKED,BUTTON3_CLICKED
|
from curses import BUTTON2_CLICKED,BUTTON3_CLICKED
|
||||||
|
|
||||||
# minimum size for UIs
|
# minimum size for UIs
|
||||||
MIN_COLS = 140
|
MIN_COLS = 120
|
||||||
MIN_LINES = 50
|
MIN_LINES = 50
|
||||||
|
|
||||||
# -------------------------------------
|
# -------------------------------------
|
||||||
@ -247,7 +247,7 @@ class SingleSelectColumns(SelectColumnBase, SingleSelectWithChanged):
|
|||||||
def h_cursor_line_right(self,ch):
|
def h_cursor_line_right(self,ch):
|
||||||
self.h_exit_down('bye bye')
|
self.h_exit_down('bye bye')
|
||||||
|
|
||||||
class TextBox(npyscreen.MultiLineEdit):
|
class TextBoxInner(npyscreen.MultiLineEdit):
|
||||||
|
|
||||||
def __init__(self,*args,**kwargs):
|
def __init__(self,*args,**kwargs):
|
||||||
super().__init__(*args,**kwargs)
|
super().__init__(*args,**kwargs)
|
||||||
@ -292,54 +292,57 @@ class TextBox(npyscreen.MultiLineEdit):
|
|||||||
if bstate & (BUTTON2_CLICKED|BUTTON3_CLICKED):
|
if bstate & (BUTTON2_CLICKED|BUTTON3_CLICKED):
|
||||||
self.h_paste()
|
self.h_paste()
|
||||||
|
|
||||||
def update(self, clear=True):
|
# def update(self, clear=True):
|
||||||
if clear:
|
# if clear:
|
||||||
self.clear()
|
# self.clear()
|
||||||
|
|
||||||
HEIGHT = self.height
|
# HEIGHT = self.height
|
||||||
WIDTH = self.width
|
# WIDTH = self.width
|
||||||
# draw box.
|
# # draw box.
|
||||||
self.parent.curses_pad.hline(self.rely, self.relx, curses.ACS_HLINE, WIDTH)
|
# self.parent.curses_pad.hline(self.rely, self.relx, curses.ACS_HLINE, WIDTH)
|
||||||
self.parent.curses_pad.hline(
|
# self.parent.curses_pad.hline(
|
||||||
self.rely + HEIGHT, self.relx, curses.ACS_HLINE, WIDTH
|
# self.rely + HEIGHT, self.relx, curses.ACS_HLINE, WIDTH
|
||||||
)
|
# )
|
||||||
self.parent.curses_pad.vline(
|
# self.parent.curses_pad.vline(
|
||||||
self.rely, self.relx, curses.ACS_VLINE, self.height
|
# self.rely, self.relx, curses.ACS_VLINE, self.height
|
||||||
)
|
# )
|
||||||
self.parent.curses_pad.vline(
|
# self.parent.curses_pad.vline(
|
||||||
self.rely, self.relx + WIDTH, curses.ACS_VLINE, HEIGHT
|
# self.rely, self.relx + WIDTH, curses.ACS_VLINE, HEIGHT
|
||||||
)
|
# )
|
||||||
|
|
||||||
# draw corners
|
# # draw corners
|
||||||
self.parent.curses_pad.addch(
|
# self.parent.curses_pad.addch(
|
||||||
self.rely,
|
# self.rely,
|
||||||
self.relx,
|
# self.relx,
|
||||||
curses.ACS_ULCORNER,
|
# curses.ACS_ULCORNER,
|
||||||
)
|
# )
|
||||||
self.parent.curses_pad.addch(
|
# self.parent.curses_pad.addch(
|
||||||
self.rely,
|
# self.rely,
|
||||||
self.relx + WIDTH,
|
# self.relx + WIDTH,
|
||||||
curses.ACS_URCORNER,
|
# curses.ACS_URCORNER,
|
||||||
)
|
# )
|
||||||
self.parent.curses_pad.addch(
|
# self.parent.curses_pad.addch(
|
||||||
self.rely + HEIGHT,
|
# self.rely + HEIGHT,
|
||||||
self.relx,
|
# self.relx,
|
||||||
curses.ACS_LLCORNER,
|
# curses.ACS_LLCORNER,
|
||||||
)
|
# )
|
||||||
self.parent.curses_pad.addch(
|
# self.parent.curses_pad.addch(
|
||||||
self.rely + HEIGHT,
|
# self.rely + HEIGHT,
|
||||||
self.relx + WIDTH,
|
# self.relx + WIDTH,
|
||||||
curses.ACS_LRCORNER,
|
# curses.ACS_LRCORNER,
|
||||||
)
|
# )
|
||||||
|
|
||||||
# fool our superclass into thinking drawing area is smaller - this is really hacky but it seems to work
|
# # fool our superclass into thinking drawing area is smaller - this is really hacky but it seems to work
|
||||||
(relx, rely, height, width) = (self.relx, self.rely, self.height, self.width)
|
# (relx, rely, height, width) = (self.relx, self.rely, self.height, self.width)
|
||||||
self.relx += 1
|
# self.relx += 1
|
||||||
self.rely += 1
|
# self.rely += 1
|
||||||
self.height -= 1
|
# self.height -= 1
|
||||||
self.width -= 1
|
# self.width -= 1
|
||||||
super().update(clear=False)
|
# super().update(clear=False)
|
||||||
(self.relx, self.rely, self.height, self.width) = (relx, rely, height, width)
|
# (self.relx, self.rely, self.height, self.width) = (relx, rely, height, width)
|
||||||
|
|
||||||
|
class TextBox(npyscreen.BoxTitle):
|
||||||
|
_contained_widget = TextBoxInner
|
||||||
|
|
||||||
class BufferBox(npyscreen.BoxTitle):
|
class BufferBox(npyscreen.BoxTitle):
|
||||||
_contained_widget = npyscreen.BufferPager
|
_contained_widget = npyscreen.BufferPager
|
||||||
@ -354,6 +357,9 @@ class ConfirmCancelPopup(fmPopup.ActionPopup):
|
|||||||
class FileBox(npyscreen.BoxTitle):
|
class FileBox(npyscreen.BoxTitle):
|
||||||
_contained_widget = npyscreen.Filename
|
_contained_widget = npyscreen.Filename
|
||||||
|
|
||||||
|
class PrettyTextBox(npyscreen.BoxTitle):
|
||||||
|
_contained_widget = TextBox
|
||||||
|
|
||||||
def _wrap_message_lines(message, line_length):
|
def _wrap_message_lines(message, line_length):
|
||||||
lines = []
|
lines = []
|
||||||
for line in message.split('\n'):
|
for line in message.split('\n'):
|
||||||
|
Loading…
Reference in New Issue
Block a user