mirror of
https://github.com/sd-webui/stable-diffusion-webui.git
synced 2024-12-14 23:02:00 +03:00
The Merge (#1730)
This commit is contained in:
commit
571fb897ed
5
.gitignore
vendored
5
.gitignore
vendored
@ -59,14 +59,17 @@ condaenv.*.requirements.txt
|
||||
!/src/components/*
|
||||
!/src/pages/*
|
||||
/src/*
|
||||
/inputs
|
||||
/outputs
|
||||
/model_cache
|
||||
/log/**/*.png
|
||||
/log/webui/*
|
||||
/log/log.csv
|
||||
/flagged/*
|
||||
/gfpgan/*
|
||||
/models/*
|
||||
/webui/flet/assets/uploads/
|
||||
/webui/flet/assets/outputs/
|
||||
|
||||
z_version_env.tmp
|
||||
scripts/bridgeData.py
|
||||
/user_data/*
|
||||
|
@ -33,6 +33,7 @@
|
||||
# -option_list
|
||||
# slider
|
||||
# -value
|
||||
# -value_type 'int', 'float'
|
||||
# -min
|
||||
# -max
|
||||
# -step
|
||||
@ -56,15 +57,20 @@ webui_page:
|
||||
default_text_size:
|
||||
display: slider
|
||||
value: !!int '20'
|
||||
value_type: 'int'
|
||||
min: !!int '10'
|
||||
max: !!int '32'
|
||||
step: !!float '2.0'
|
||||
max_message_history:
|
||||
display: slider
|
||||
value: !!int '20'
|
||||
value_type: 'int'
|
||||
min: !!int '1'
|
||||
max: !!int '100'
|
||||
step: !!int '1'
|
||||
theme_color:
|
||||
display: textinput
|
||||
value: !!str 'blue'
|
||||
|
||||
general_page:
|
||||
huggingface_token:
|
||||
@ -176,7 +182,9 @@ performance_page:
|
||||
keep_all_models_loaded:
|
||||
display: bool
|
||||
value: !!bool 'false'
|
||||
#no_verify_input: False
|
||||
no_verify_input:
|
||||
display: bool
|
||||
value: !!bool 'false'
|
||||
#no_half: False
|
||||
#use_float16: False
|
||||
#precision: "autocast"
|
||||
|
@ -56,6 +56,8 @@ general:
|
||||
grid_quality: 95
|
||||
n_rows: -1
|
||||
no_verify_input: False
|
||||
show_percent_in_tab_title: True
|
||||
enable_suggestions: True
|
||||
no_half: False
|
||||
use_float16: False
|
||||
precision: "autocast"
|
||||
@ -427,3 +429,11 @@ model_manager:
|
||||
ldsr_model:
|
||||
file_name: "model.ckpt"
|
||||
download_link: "https://heibox.uni-heidelberg.de/f/578df07c8fc04ffbadf3/?dl=1"
|
||||
|
||||
sygil:
|
||||
model_name: "Sygil Diffusion 0.1"
|
||||
save_location: "./models/custom"
|
||||
files:
|
||||
sd_wd_ld_trinart_merged:
|
||||
file_name: "sygil-diffusion-v0.1_365608_lora.ckpt"
|
||||
download_link: "https://huggingface.co/Sygil/Sygil-Diffusion-v0.1/resolve/main/sygil-diffusion-v0.1_365608_lora.ckpt"
|
@ -208,6 +208,14 @@ def layout():
|
||||
|
||||
st.session_state["defaults"].general.no_verify_input = st.checkbox("Do not Verify Input", value=st.session_state['defaults'].general.no_verify_input,
|
||||
help="Do not verify input to check if it's too long. Default: False")
|
||||
|
||||
st.session_state["defaults"].general.show_percent_in_tab_title = st.checkbox("Show Percent in tab title", value=st.session_state['defaults'].general.show_percent_in_tab_title,
|
||||
help="Add the progress percent value to the page title on the tab on your browser. "
|
||||
"This is useful in case you need to know how the generation is going while doign something else"
|
||||
"in another tab on your browser. Default: True")
|
||||
|
||||
st.session_state["defaults"].general.enable_suggestions = st.checkbox("Enable Suggestions Box", value=st.session_state['defaults'].general.enable_suggestions,
|
||||
help="Adds a suggestion box under the prompt when clicked. Default: True")
|
||||
|
||||
st.session_state["defaults"].daisi_app.running_on_daisi_io = st.checkbox("Running on Daisi.io?", value=st.session_state['defaults'].daisi_app.running_on_daisi_io,
|
||||
help="Specify if we are running on app.Daisi.io . Default: False")
|
||||
|
@ -18,7 +18,7 @@ from sd_utils import st, server_state, no_rerun, \
|
||||
generation_callback, process_images, KDiffusionSampler, \
|
||||
custom_models_available, RealESRGAN_available, GFPGAN_available, \
|
||||
LDSR_available, load_models, hc, seed_to_int, logger, \
|
||||
resize_image, get_matched_noise, CFGMaskedDenoiser, ImageFilter
|
||||
resize_image, get_matched_noise, CFGMaskedDenoiser, ImageFilter, set_page_title
|
||||
|
||||
# streamlit imports
|
||||
from streamlit.runtime.scriptrunner import StopException
|
||||
@ -82,6 +82,8 @@ def img2img(prompt: str = '', init_info: any = None, init_info_mask: any = None,
|
||||
sampler = KDiffusionSampler(server_state["model"],'dpm_2_ancestral')
|
||||
elif sampler_name == 'k_dpm_2':
|
||||
sampler = KDiffusionSampler(server_state["model"],'dpm_2')
|
||||
elif sampler_name == 'k_dpmpp_2m':
|
||||
sampler = KDiffusionSampler(server_state["model"],'dpmpp_2m')
|
||||
elif sampler_name == 'k_euler_a':
|
||||
sampler = KDiffusionSampler(server_state["model"],'euler_ancestral')
|
||||
elif sampler_name == 'k_euler':
|
||||
@ -377,7 +379,10 @@ def layout():
|
||||
#prompt = st.text_area("Input Text","")
|
||||
placeholder = "A corgi wearing a top hat as an oil painting."
|
||||
prompt = st.text_area("Input Text","", placeholder=placeholder, height=54)
|
||||
sygil_suggestions.suggestion_area(placeholder)
|
||||
|
||||
if "defaults" in st.session_state:
|
||||
if st.session_state["defaults"].general.enable_suggestions:
|
||||
sygil_suggestions.suggestion_area(placeholder)
|
||||
|
||||
if "defaults" in st.session_state:
|
||||
if st.session_state['defaults'].admin.global_negative_prompt:
|
||||
@ -411,7 +416,7 @@ def layout():
|
||||
min_value=st.session_state['defaults'].img2img.sampling_steps.min_value,
|
||||
step=st.session_state['defaults'].img2img.sampling_steps.step)
|
||||
|
||||
sampler_name_list = ["k_lms", "k_euler", "k_euler_a", "k_dpm_2", "k_dpm_2_a", "k_heun", "PLMS", "DDIM"]
|
||||
sampler_name_list = ["k_lms", "k_euler", "k_euler_a", "k_dpm_2", "k_dpm_2_a", "k_dpmpp_2m", "k_heun", "PLMS", "DDIM"]
|
||||
st.session_state["sampler_name"] = st.selectbox("Sampling method",sampler_name_list,
|
||||
index=sampler_name_list.index(st.session_state['defaults'].img2img.sampler_name), help="Sampling method to use.")
|
||||
|
||||
@ -733,8 +738,12 @@ def layout():
|
||||
#show a message when the generation is complete.
|
||||
message.success('Render Complete: ' + info + '; Stats: ' + stats, icon="✅")
|
||||
|
||||
except (StopException, KeyError):
|
||||
except (StopException,
|
||||
#KeyError
|
||||
):
|
||||
logger.info(f"Received Streamlit StopException")
|
||||
# reset the page title so the percent doesnt stay on it confusing the user.
|
||||
set_page_title(f"Stable Diffusion Playground")
|
||||
|
||||
# this will render all the images at the end of the generation but its better if its moved to a second tab inside col2 and shown as a gallery.
|
||||
# use the current col2 first tab to show the preview_img and update it as its generated.
|
||||
|
@ -109,6 +109,9 @@ try:
|
||||
except:
|
||||
pass
|
||||
|
||||
# disable diffusers telemetry
|
||||
os.environ["DISABLE_TELEMETRY"] = "YES"
|
||||
|
||||
# remove some annoying deprecation warnings that show every now and then.
|
||||
warnings.filterwarnings("ignore", category=DeprecationWarning)
|
||||
warnings.filterwarnings("ignore", category=UserWarning)
|
||||
@ -941,7 +944,7 @@ class LDSR():
|
||||
global_step = pl_sd["global_step"]
|
||||
sd = pl_sd["state_dict"]
|
||||
config = OmegaConf.load(self.yamlPath)
|
||||
model = instantiate_from_config(config.model)
|
||||
model = instantiate_from_config(config.model, personalization_config="")
|
||||
m, u = model.load_state_dict(sd, strict=False)
|
||||
model.cuda()
|
||||
model.eval()
|
||||
@ -1692,6 +1695,8 @@ def generation_callback(img, i=0):
|
||||
if "progress_bar" in st.session_state:
|
||||
try:
|
||||
st.session_state["progress_bar"].progress(percent if percent < 100 else 100)
|
||||
if st.session_state["defaults"].general.show_percent_in_tab_title:
|
||||
set_page_title(f"({percent if percent < 100 else 100}%) Stable Diffusion Playground")
|
||||
except UnboundLocalError as e:
|
||||
#logger.error(e)
|
||||
pass
|
||||
|
@ -19,7 +19,7 @@ from sd_utils import st, MemUsageMonitor, server_state, no_rerun, \
|
||||
save_sample, generation_callback, process_images, \
|
||||
KDiffusionSampler, \
|
||||
custom_models_available, RealESRGAN_available, GFPGAN_available, \
|
||||
LDSR_available, load_models, hc, seed_to_int, logger
|
||||
LDSR_available, load_models, hc, seed_to_int, logger, set_page_title
|
||||
|
||||
# streamlit imports
|
||||
from streamlit.runtime.scriptrunner import StopException
|
||||
@ -316,6 +316,8 @@ def txt2img(prompt: str, ddim_steps: int, sampler_name: str, n_iter: int, batch_
|
||||
sampler = KDiffusionSampler(server_state["model"],'dpm_2_ancestral')
|
||||
elif sampler_name == 'k_dpm_2':
|
||||
sampler = KDiffusionSampler(server_state["model"],'dpm_2')
|
||||
elif sampler_name == 'k_dpmpp_2m':
|
||||
sampler = KDiffusionSampler(server_state["model"],'dpmpp_2m')
|
||||
elif sampler_name == 'k_euler_a':
|
||||
sampler = KDiffusionSampler(server_state["model"],'euler_ancestral')
|
||||
elif sampler_name == 'k_euler':
|
||||
@ -425,7 +427,10 @@ def layout():
|
||||
#prompt = st.text_area("Input Text","")
|
||||
placeholder = "A corgi wearing a top hat as an oil painting."
|
||||
prompt = st.text_area("Input Text","", placeholder=placeholder, height=54)
|
||||
sygil_suggestions.suggestion_area(placeholder)
|
||||
|
||||
if "defaults" in st.session_state:
|
||||
if st.session_state["defaults"].general.enable_suggestions:
|
||||
sygil_suggestions.suggestion_area(placeholder)
|
||||
|
||||
if "defaults" in st.session_state:
|
||||
if st.session_state['defaults'].admin.global_negative_prompt:
|
||||
@ -519,7 +524,7 @@ def layout():
|
||||
step=st.session_state['defaults'].txt2img.sampling_steps.step,
|
||||
help="Set the default number of sampling steps to use. Default is: 30 (with k_euler)")
|
||||
|
||||
sampler_name_list = ["k_lms", "k_euler", "k_euler_a", "k_dpm_2", "k_dpm_2_a", "k_heun", "PLMS", "DDIM"]
|
||||
sampler_name_list = ["k_lms", "k_euler", "k_euler_a", "k_dpm_2", "k_dpm_2_a", "k_dpmpp_2m", "k_heun", "PLMS", "DDIM"]
|
||||
sampler_name = st.selectbox("Sampling method", sampler_name_list,
|
||||
index=sampler_name_list.index(st.session_state['defaults'].txt2img.default_sampler), help="Sampling method to use. Default: k_euler")
|
||||
|
||||
@ -668,30 +673,35 @@ def layout():
|
||||
|
||||
#print(st.session_state['use_RealESRGAN'])
|
||||
#print(st.session_state['use_LDSR'])
|
||||
#try:
|
||||
#
|
||||
try:
|
||||
|
||||
|
||||
output_images, seeds, info, stats = txt2img(prompt, st.session_state.sampling_steps, sampler_name, st.session_state["batch_count"], st.session_state["batch_size"],
|
||||
cfg_scale, seed, height, width, separate_prompts, normalize_prompt_weights, save_individual_images,
|
||||
save_grid, group_by_prompt, save_as_jpg, st.session_state["use_GFPGAN"], st.session_state['GFPGAN_model'],
|
||||
use_RealESRGAN=st.session_state["use_RealESRGAN"], RealESRGAN_model=st.session_state["RealESRGAN_model"],
|
||||
use_LDSR=st.session_state["use_LDSR"], LDSR_model=st.session_state["LDSR_model"],
|
||||
variant_amount=variant_amount, variant_seed=variant_seed, write_info_files=write_info_files,
|
||||
use_stable_horde=use_stable_horde, stable_horde_key=stable_horde_key)
|
||||
output_images, seeds, info, stats = txt2img(prompt, st.session_state.sampling_steps, sampler_name, st.session_state["batch_count"], st.session_state["batch_size"],
|
||||
cfg_scale, seed, height, width, separate_prompts, normalize_prompt_weights, save_individual_images,
|
||||
save_grid, group_by_prompt, save_as_jpg, st.session_state["use_GFPGAN"], st.session_state['GFPGAN_model'],
|
||||
use_RealESRGAN=st.session_state["use_RealESRGAN"], RealESRGAN_model=st.session_state["RealESRGAN_model"],
|
||||
use_LDSR=st.session_state["use_LDSR"], LDSR_model=st.session_state["LDSR_model"],
|
||||
variant_amount=variant_amount, variant_seed=variant_seed, write_info_files=write_info_files,
|
||||
use_stable_horde=use_stable_horde, stable_horde_key=stable_horde_key)
|
||||
|
||||
message.success('Render Complete: ' + info + '; Stats: ' + stats, icon="✅")
|
||||
|
||||
with gallery_tab:
|
||||
logger.info(seeds)
|
||||
st.session_state["gallery"].text = ""
|
||||
sdGallery(output_images)
|
||||
|
||||
|
||||
message.success('Render Complete: ' + info + '; Stats: ' + stats, icon="✅")
|
||||
|
||||
with gallery_tab:
|
||||
logger.info(seeds)
|
||||
st.session_state["gallery"].text = ""
|
||||
sdGallery(output_images)
|
||||
|
||||
|
||||
#except (StopException, KeyError):
|
||||
#print(f"Received Streamlit StopException")
|
||||
|
||||
# this will render all the images at the end of the generation but its better if its moved to a second tab inside col2 and shown as a gallery.
|
||||
# use the current col2 first tab to show the preview_img and update it as its generated.
|
||||
#preview_image.image(output_images)
|
||||
except (StopException,
|
||||
#KeyError
|
||||
):
|
||||
print(f"Received Streamlit StopException")
|
||||
|
||||
# reset the page title so the percent doesnt stay on it confusing the user.
|
||||
set_page_title(f"Stable Diffusion Playground")
|
||||
|
||||
# this will render all the images at the end of the generation but its better if its moved to a second tab inside col2 and shown as a gallery.
|
||||
# use the current col2 first tab to show the preview_img and update it as its generated.
|
||||
#preview_image.image(output_images)
|
||||
|
||||
|
||||
|
@ -24,7 +24,7 @@ https://gist.github.com/karpathy/00103b0037c5aaea32fe1da1af553355
|
||||
from sd_utils import st, MemUsageMonitor, server_state, no_rerun, torch_gc, \
|
||||
custom_models_available, RealESRGAN_available, GFPGAN_available, \
|
||||
LDSR_available, hc, seed_to_int, logger, slerp, optimize_update_preview_frequency, \
|
||||
load_learned_embed_in_clip, load_GFPGAN, RealESRGANModel
|
||||
load_learned_embed_in_clip, load_GFPGAN, RealESRGANModel, set_page_title
|
||||
|
||||
|
||||
# streamlit imports
|
||||
@ -942,12 +942,12 @@ class StableDiffusionWalkPipeline(DiffusionPipeline):
|
||||
def diffuse(
|
||||
pipe,
|
||||
cond_embeddings, # text conditioning, should be (1, 77, 768)
|
||||
cond_latents, # image conditioning, should be (1, 4, 64, 64)
|
||||
num_inference_steps,
|
||||
cfg_scale,
|
||||
eta,
|
||||
fps=30
|
||||
):
|
||||
cond_latents, # image conditioning, should be (1, 4, 64, 64)
|
||||
num_inference_steps,
|
||||
cfg_scale,
|
||||
eta,
|
||||
fps=30
|
||||
):
|
||||
|
||||
torch_device = cond_latents.get_device()
|
||||
|
||||
@ -1082,6 +1082,9 @@ def diffuse(
|
||||
|
||||
if "progress_bar" in st.session_state:
|
||||
st.session_state["progress_bar"].progress(total_percent if total_percent < 100 else 100)
|
||||
|
||||
if st.session_state["defaults"].general.show_percent_in_tab_title:
|
||||
set_page_title(f"({percent if percent < 100 else 100}%) Stable Diffusion Playground")
|
||||
|
||||
except KeyError:
|
||||
raise StopException
|
||||
@ -1130,7 +1133,7 @@ def load_diffusers_model(weights_path,torch_device):
|
||||
if not os.path.exists(model_path + "/model_index.json"):
|
||||
server_state["pipe"] = StableDiffusionPipeline.from_pretrained(
|
||||
weights_path,
|
||||
use_local_file=True,
|
||||
#use_local_file=True,
|
||||
use_auth_token=st.session_state["defaults"].general.huggingface_token,
|
||||
torch_dtype=torch.float16 if st.session_state['defaults'].general.use_float16 else None,
|
||||
revision="fp16" if not st.session_state['defaults'].general.no_half else None,
|
||||
@ -1143,7 +1146,7 @@ def load_diffusers_model(weights_path,torch_device):
|
||||
else:
|
||||
server_state["pipe"] = StableDiffusionPipeline.from_pretrained(
|
||||
model_path,
|
||||
use_local_file=True,
|
||||
#use_local_file=True,
|
||||
torch_dtype=torch.float16 if st.session_state['defaults'].general.use_float16 else None,
|
||||
revision="fp16" if not st.session_state['defaults'].general.no_half else None,
|
||||
safety_checker=None, # Very important for videos...lots of false positives while interpolating
|
||||
@ -1178,7 +1181,8 @@ def load_diffusers_model(weights_path,torch_device):
|
||||
server_state['float16'] = st.session_state['defaults'].general.use_float16
|
||||
server_state['no_half'] = st.session_state['defaults'].general.no_half
|
||||
server_state['optimized'] = st.session_state['defaults'].general.optimized
|
||||
|
||||
|
||||
#with no_rerun:
|
||||
load_diffusers_model(weights_path, torch_device)
|
||||
else:
|
||||
logger.info("Tx2Vid Model already Loaded")
|
||||
@ -1318,28 +1322,28 @@ def txt2vid(
|
||||
with open(os.path.join(full_path , f'{slugify(str(seeds))}_config.json' if len(prompts) > 1 else "prompts_config.json"), "w") as outfile:
|
||||
outfile.write(json.dumps(
|
||||
dict(
|
||||
prompts = prompts,
|
||||
gpu = gpu,
|
||||
num_steps = num_steps,
|
||||
max_duration_in_seconds = max_duration_in_seconds,
|
||||
num_inference_steps = num_inference_steps,
|
||||
cfg_scale = cfg_scale,
|
||||
do_loop = do_loop,
|
||||
use_lerp_for_text = use_lerp_for_text,
|
||||
seeds = seeds,
|
||||
quality = quality,
|
||||
eta = eta,
|
||||
width = width,
|
||||
height = height,
|
||||
weights_path = weights_path,
|
||||
scheduler=scheduler,
|
||||
disable_tqdm = disable_tqdm,
|
||||
beta_start = beta_start,
|
||||
beta_end = beta_end,
|
||||
beta_schedule = beta_schedule
|
||||
),
|
||||
indent=2,
|
||||
sort_keys=False,
|
||||
prompts = prompts,
|
||||
gpu = gpu,
|
||||
num_steps = num_steps,
|
||||
max_duration_in_seconds = max_duration_in_seconds,
|
||||
num_inference_steps = num_inference_steps,
|
||||
cfg_scale = cfg_scale,
|
||||
do_loop = do_loop,
|
||||
use_lerp_for_text = use_lerp_for_text,
|
||||
seeds = seeds,
|
||||
quality = quality,
|
||||
eta = eta,
|
||||
width = width,
|
||||
height = height,
|
||||
weights_path = weights_path,
|
||||
scheduler=scheduler,
|
||||
disable_tqdm = disable_tqdm,
|
||||
beta_start = beta_start,
|
||||
beta_end = beta_end,
|
||||
beta_schedule = beta_schedule
|
||||
),
|
||||
indent=2,
|
||||
sort_keys=False,
|
||||
))
|
||||
|
||||
#print(scheduler)
|
||||
@ -1383,10 +1387,11 @@ def txt2vid(
|
||||
#flaxddpms=flaxddpms_scheduler,
|
||||
#flaxpndms=flaxpndms_scheduler,
|
||||
)
|
||||
|
||||
with st.session_state["progress_bar_text"].container():
|
||||
with hc.HyLoader('Loading Models...', hc.Loaders.standard_loaders,index=[0]):
|
||||
load_diffusers_model(weights_path, torch_device)
|
||||
|
||||
with no_rerun:
|
||||
with st.session_state["progress_bar_text"].container():
|
||||
with hc.HyLoader('Loading Models...', hc.Loaders.standard_loaders,index=[0]):
|
||||
load_diffusers_model(weights_path, torch_device)
|
||||
|
||||
if "pipe" not in server_state:
|
||||
logger.error('wtf')
|
||||
@ -1593,6 +1598,9 @@ def txt2vid(
|
||||
video_path = save_video_to_disk(frames, seeds, sanitized_prompt, save_video=save_video, outdir=outdir)
|
||||
|
||||
except StopException:
|
||||
# reset the page title so the percent doesnt stay on it confusing the user.
|
||||
set_page_title(f"Stable Diffusion Playground")
|
||||
|
||||
if save_video_on_stop:
|
||||
logger.info("Streamlit Stop Exception Received. Saving video")
|
||||
video_path = save_video_to_disk(frames, seeds, sanitized_prompt, save_video=save_video, outdir=outdir)
|
||||
@ -1626,7 +1634,10 @@ def layout():
|
||||
#prompt = st.text_area("Input Text","")
|
||||
placeholder = "A corgi wearing a top hat as an oil painting."
|
||||
prompt = st.text_area("Input Text","", placeholder=placeholder, height=54)
|
||||
sygil_suggestions.suggestion_area(placeholder)
|
||||
|
||||
if "defaults" in st.session_state:
|
||||
if st.session_state["defaults"].general.enable_suggestions:
|
||||
sygil_suggestions.suggestion_area(placeholder)
|
||||
|
||||
if "defaults" in st.session_state:
|
||||
if st.session_state['defaults'].admin.global_negative_prompt:
|
||||
@ -1909,25 +1920,25 @@ def layout():
|
||||
#print("Loading models")
|
||||
# load the models when we hit the generate button for the first time, it wont be loaded after that so dont worry.
|
||||
#load_models(False, st.session_state["use_GFPGAN"], True, st.session_state["RealESRGAN_model"])
|
||||
with no_rerun:
|
||||
if st.session_state["use_GFPGAN"]:
|
||||
if "GFPGAN" in server_state:
|
||||
logger.info("GFPGAN already loaded")
|
||||
else:
|
||||
with col2:
|
||||
with hc.HyLoader('Loading Models...', hc.Loaders.standard_loaders,index=[0]):
|
||||
# Load GFPGAN
|
||||
if os.path.exists(st.session_state["defaults"].general.GFPGAN_dir):
|
||||
try:
|
||||
load_GFPGAN()
|
||||
logger.info("Loaded GFPGAN")
|
||||
except Exception:
|
||||
import traceback
|
||||
logger.error("Error loading GFPGAN:", file=sys.stderr)
|
||||
logger.error(traceback.format_exc(), file=sys.stderr)
|
||||
#with no_rerun:
|
||||
if st.session_state["use_GFPGAN"]:
|
||||
if "GFPGAN" in server_state:
|
||||
logger.info("GFPGAN already loaded")
|
||||
else:
|
||||
if "GFPGAN" in server_state:
|
||||
del server_state["GFPGAN"]
|
||||
with col2:
|
||||
with hc.HyLoader('Loading Models...', hc.Loaders.standard_loaders,index=[0]):
|
||||
# Load GFPGAN
|
||||
if os.path.exists(st.session_state["defaults"].general.GFPGAN_dir):
|
||||
try:
|
||||
load_GFPGAN()
|
||||
logger.info("Loaded GFPGAN")
|
||||
except Exception:
|
||||
import traceback
|
||||
logger.error("Error loading GFPGAN:", file=sys.stderr)
|
||||
logger.error(traceback.format_exc(), file=sys.stderr)
|
||||
else:
|
||||
if "GFPGAN" in server_state:
|
||||
del server_state["GFPGAN"]
|
||||
|
||||
#try:
|
||||
# run video generation
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,65 +0,0 @@
|
||||
# webui_utils.py
|
||||
|
||||
# imports
|
||||
import os, yaml
|
||||
from PIL import Image
|
||||
from pprint import pprint
|
||||
|
||||
|
||||
# logging
|
||||
log_file = 'webui_flet.log'
|
||||
|
||||
def log_message(message):
|
||||
with open(log_file,'a+') as log:
|
||||
log.write(message)
|
||||
|
||||
|
||||
# Settings
|
||||
path_to_default_config = 'configs/webui/webui_flet.yaml'
|
||||
path_to_user_config = 'configs/webui/userconfig_flet.yaml'
|
||||
|
||||
def get_default_settings_from_config():
|
||||
with open(path_to_default_config) as f:
|
||||
default_settings = yaml.safe_load(f)
|
||||
return default_settings
|
||||
|
||||
def get_user_settings_from_config():
|
||||
settings = get_default_settings_from_config()
|
||||
if os.path.exists(path_to_user_config):
|
||||
with open(path_to_user_config) as f:
|
||||
user_settings = yaml.safe_load(f)
|
||||
settings.update(user_settings)
|
||||
return settings
|
||||
|
||||
def save_user_settings_to_config(settings):
|
||||
with open(path_to_user_config, 'w+') as f:
|
||||
yaml.dump(settings, f, default_flow_style=False)
|
||||
|
||||
|
||||
# Image handling
|
||||
|
||||
def load_images(images): # just for testing, needs love to function
|
||||
images_loaded = {}
|
||||
images_not_loaded = []
|
||||
for i in images:
|
||||
try:
|
||||
img = Image.open(images[i]['path'])
|
||||
if img:
|
||||
images_loaded.update({images[i].name:img})
|
||||
except:
|
||||
images_not_loaded.append(i)
|
||||
|
||||
return images_loaded, images_not_loaded
|
||||
|
||||
def create_blank_image():
|
||||
img = Image.new('RGBA',(512,512),(0,0,0,0))
|
||||
return img
|
||||
|
||||
|
||||
# Textual Inversion
|
||||
textual_inversion_grid_row_list = [
|
||||
'model', 'medium', 'artist', 'trending', 'movement', 'flavors', 'techniques', 'tags',
|
||||
]
|
||||
|
||||
def run_textual_inversion(args):
|
||||
pass
|
BIN
webui/flet/assets/icons/favicon.png
Normal file
BIN
webui/flet/assets/icons/favicon.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 90 KiB |
BIN
webui/flet/assets/images/chickens.jpg
Normal file
BIN
webui/flet/assets/images/chickens.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.3 MiB |
BIN
webui/flet/assets/images/default_grid_texture.png
Executable file
BIN
webui/flet/assets/images/default_grid_texture.png
Executable file
Binary file not shown.
After Width: | Height: | Size: 3.9 KiB |
35
webui/flet/assets/manifest.json
Normal file
35
webui/flet/assets/manifest.json
Normal file
@ -0,0 +1,35 @@
|
||||
{
|
||||
"name": "Sygil",
|
||||
"short_name": "Sygil",
|
||||
"start_url": ".",
|
||||
"display": "standalone",
|
||||
"background_color": "#ffffff",
|
||||
"theme_color": "#5175C2",
|
||||
"description": "This is a customized Flet app description.",
|
||||
"orientation": "natural",
|
||||
"prefer_related_applications": false,
|
||||
"icons": [
|
||||
{
|
||||
"src": "icons/icon-192.png",
|
||||
"sizes": "192x192",
|
||||
"type": "image/png"
|
||||
},
|
||||
{
|
||||
"src": "icons/icon-512.png",
|
||||
"sizes": "512x512",
|
||||
"type": "image/png"
|
||||
},
|
||||
{
|
||||
"src": "icons/icon-maskable-192.png",
|
||||
"sizes": "192x192",
|
||||
"type": "image/png",
|
||||
"purpose": "maskable"
|
||||
},
|
||||
{
|
||||
"src": "icons/icon-maskable-512.png",
|
||||
"sizes": "512x512",
|
||||
"type": "image/png",
|
||||
"purpose": "maskable"
|
||||
}
|
||||
]
|
||||
}
|
4
webui/flet/scripts/__init__.py
Normal file
4
webui/flet/scripts/__init__.py
Normal file
@ -0,0 +1,4 @@
|
||||
# other imports
|
||||
from math import pi
|
||||
from typing import Optional
|
||||
from loguru import logger
|
430
webui/flet/scripts/flet_asset_manager.py
Normal file
430
webui/flet/scripts/flet_asset_manager.py
Normal file
@ -0,0 +1,430 @@
|
||||
# flet_layer_manager.py
|
||||
|
||||
# Flet imports
|
||||
import flet as ft
|
||||
|
||||
# utils imports
|
||||
from scripts import flet_utils
|
||||
|
||||
|
||||
class AssetManager(ft.Container):
|
||||
def setup(self):
|
||||
self.width = self.page.left_panel_width
|
||||
self.bgcolor = self.page.primary_color
|
||||
self.padding = self.page.container_padding
|
||||
self.margin = self.page.container_margin
|
||||
|
||||
self.set_tab_text_size(self.page.text_size)
|
||||
self.set_tab_bgcolor(self.page.secondary_color)
|
||||
self.set_tab_padding(self.page.container_padding)
|
||||
self.set_tab_margin(self.page.container_margin)
|
||||
|
||||
self.dragbar.content.width = self.page.vertical_divider_width
|
||||
self.dragbar.content.color = self.page.tertiary_color
|
||||
|
||||
def on_page_change(self):
|
||||
self.width = self.page.left_panel_width
|
||||
self.bgcolor = self.page.primary_color
|
||||
self.padding = self.page.container_padding
|
||||
self.margin = self.page.container_margin
|
||||
|
||||
self.set_tab_text_size(self.page.text_size)
|
||||
self.set_tab_bgcolor(self.page.secondary_color)
|
||||
self.set_tab_padding(self.page.container_padding)
|
||||
self.set_tab_margin(self.page.container_margin)
|
||||
|
||||
self.dragbar.content.width = self.page.vertical_divider_width
|
||||
self.dragbar.content.color = self.page.tertiary_color
|
||||
|
||||
if self.page.active_layer is not None:
|
||||
self.page.active_layer.handle.color = self.page.tertiary_color
|
||||
|
||||
def add_image_as_layer(self, image):
|
||||
return self.layer_panel.add_image_as_layer(image)
|
||||
|
||||
def add_images_as_layers(self, images):
|
||||
return self.layer_panel.add_images_as_layers(images)
|
||||
|
||||
def set_tab_text_size(self, size):
|
||||
for tab in self.tabs:
|
||||
tab.tab_content.size = size
|
||||
|
||||
def set_tab_bgcolor(self, color):
|
||||
for tab in self.tabs:
|
||||
tab.content.bgcolor = color
|
||||
|
||||
def set_tab_padding(self, padding):
|
||||
for tab in self.tabs:
|
||||
tab.content.padding = padding
|
||||
|
||||
def set_tab_margin(self, margin):
|
||||
for tab in self.tabs:
|
||||
tab.content.margin = margin
|
||||
|
||||
def resize_asset_manager(self, e: ft.DragUpdateEvent):
|
||||
self.page.left_panel_width = max(250, self.page.left_panel_width + e.delta_x)
|
||||
self.width = self.page.left_panel_width
|
||||
self.page.update()
|
||||
|
||||
def refresh_layers(self):
|
||||
self.layer_panel.refresh_layers()
|
||||
|
||||
|
||||
class AssetPanel(ft.Container):
|
||||
pass
|
||||
|
||||
|
||||
class LayerPanel(ft.Container):
|
||||
def refresh_layers(self):
|
||||
self.layers = self.content.content.controls
|
||||
self.refresh_layer_indexes()
|
||||
self.refresh_visible_layers()
|
||||
self.update()
|
||||
|
||||
def refresh_layer_indexes(self):
|
||||
count = 0
|
||||
for layer in self.layers:
|
||||
layer.index = count
|
||||
count += 1
|
||||
|
||||
def refresh_visible_layers(self):
|
||||
self.page.visible_layers = []
|
||||
for layer in self.layers:
|
||||
if not layer.disabled:
|
||||
self.page.visible_layers.append(layer)
|
||||
|
||||
def refresh_layer_name(self, e):
|
||||
self.page.refresh_layers()
|
||||
|
||||
def make_layer_active(self, index):
|
||||
for i, layer in enumerate(self.layers):
|
||||
layer.active = False
|
||||
layer.handle.color = None
|
||||
if i == index:
|
||||
layer.active = True
|
||||
layer.handle.color = self.page.tertiary_color
|
||||
self.page.set_active_layer(layer)
|
||||
|
||||
def add_layer_slot(self, image):
|
||||
label = ft.TextField(
|
||||
value = image.filename,
|
||||
focused_border_color = self.page.tertiary_color,
|
||||
text_size = self.page.text_size,
|
||||
content_padding = ft.padding.only(left = 12, top = 0, right = 0, bottom = 0),
|
||||
expand = True,
|
||||
on_submit = self.refresh_layer_name,
|
||||
)
|
||||
handle = ft.Icon(
|
||||
name = ft.icons.DRAG_HANDLE,
|
||||
color = None,
|
||||
tooltip = 'drag to move',
|
||||
)
|
||||
layer_slot = LayerSlot(
|
||||
content = ft.Row(
|
||||
controls = [
|
||||
label,
|
||||
handle,
|
||||
],
|
||||
expand = True,
|
||||
),
|
||||
height = self.page.layer_height,
|
||||
padding = 0,
|
||||
margin = 0,
|
||||
)
|
||||
layer_slot.label = label
|
||||
layer_slot.handle = handle
|
||||
layer_slot.index = 0
|
||||
layer_slot.disabled = False
|
||||
layer_slot.active = False
|
||||
layer_slot.image = image
|
||||
layer_slot.layer_image = None
|
||||
self.content.content.controls.insert(0,layer_slot)
|
||||
self.layers = self.content.content.controls
|
||||
self.refresh_layer_indexes()
|
||||
self.make_layer_active(0)
|
||||
return layer_slot
|
||||
|
||||
def add_image_as_layer(self, image):
|
||||
return self.add_layer_slot(image)
|
||||
|
||||
def add_images_as_layers(self, images):
|
||||
layer_slots = []
|
||||
for image in images:
|
||||
layer_slots.append(self.add_image_as_layer(image))
|
||||
return layer_slots
|
||||
|
||||
def get_layer_index_from_position(self, pos):
|
||||
index = int(pos / self.page.layer_height)
|
||||
return index
|
||||
|
||||
def move_layer(self, layer, index):
|
||||
if index > len(self.layers):
|
||||
layer = self.layers.pop(layer.index)
|
||||
self.layers.append(layer)
|
||||
if layer.index < index:
|
||||
index -= 1
|
||||
layer = self.layers.pop(layer.index)
|
||||
self.layers.insert(index, layer)
|
||||
self.page.refresh_layers()
|
||||
|
||||
def delete_layer(self, layer):
|
||||
if not layer:
|
||||
return
|
||||
self.layers.pop(layer.index)
|
||||
|
||||
|
||||
class LayerSlot(ft.Container):
|
||||
pass
|
||||
|
||||
|
||||
class LayerActionMenu(ft.Card):
|
||||
def show_menu(self):
|
||||
self.visible = True
|
||||
self.update()
|
||||
|
||||
def hide_menu(self):
|
||||
self.visible = False
|
||||
self.update()
|
||||
|
||||
|
||||
def close_menu(e):
|
||||
layer_action_menu.hide_menu()
|
||||
|
||||
def show_hide_layer(e):
|
||||
e.page.active_layer.disabled = False if e.page.active_layer.disabled else True
|
||||
e.page.refresh_layers()
|
||||
close_menu(e)
|
||||
|
||||
def move_layer_to_top(e):
|
||||
layer_panel.move_layer(e.page.active_layer, 0)
|
||||
close_menu(e)
|
||||
|
||||
def move_layer_up(e):
|
||||
layer_panel.move_layer(e.page.active_layer, e.page.active_layer.index - 1)
|
||||
close_menu(e)
|
||||
|
||||
def move_layer_down(e):
|
||||
layer_panel.move_layer(e.page.active_layer, e.page.active_layer.index + 2)
|
||||
close_menu(e)
|
||||
|
||||
def delete_layer(e):
|
||||
layer_panel.delete_layer(e.page.active_layer)
|
||||
e.page.active_layer = None
|
||||
e.page.refresh_layers()
|
||||
close_menu(e)
|
||||
|
||||
|
||||
class LayerAction():
|
||||
def __init__(self, text, on_click):
|
||||
self.text = text
|
||||
self.on_click = on_click
|
||||
|
||||
layer_action_list = [
|
||||
LayerAction('Show/Hide Layer', show_hide_layer),
|
||||
LayerAction('Move Layer To Top', move_layer_to_top),
|
||||
LayerAction('Move Layer Up', move_layer_up),
|
||||
LayerAction('Move Layer Down', move_layer_down),
|
||||
LayerAction('Delete Layer', delete_layer),
|
||||
]
|
||||
|
||||
def make_action_buttons(action_list):
|
||||
button_list = []
|
||||
for action in action_list:
|
||||
button_list.append(
|
||||
ft.TextButton(
|
||||
text = action.text,
|
||||
on_click = action.on_click,
|
||||
)
|
||||
)
|
||||
return button_list
|
||||
|
||||
# LayerActionMenu == ft.Card
|
||||
layer_action_menu = LayerActionMenu(
|
||||
content = ft.GestureDetector(
|
||||
content = ft.Column(
|
||||
controls = make_action_buttons(layer_action_list),
|
||||
expand = False,
|
||||
spacing = 0,
|
||||
alignment = 'start',
|
||||
tight = True,
|
||||
),
|
||||
on_exit = close_menu,
|
||||
),
|
||||
margin = 0,
|
||||
visible = False,
|
||||
)
|
||||
|
||||
|
||||
|
||||
def layer_left_click(e: ft.TapEvent):
|
||||
index = layer_panel.get_layer_index_from_position(e.local_y)
|
||||
if index >= len(layer_panel.layers):
|
||||
return
|
||||
layer_panel.make_layer_active(index)
|
||||
layer_panel.update()
|
||||
|
||||
def layer_right_click(e: ft.TapEvent):
|
||||
index = layer_panel.get_layer_index_from_position(e.local_y)
|
||||
if index >= len(layer_panel.layers):
|
||||
return
|
||||
layer_panel.make_layer_active(index)
|
||||
layer_panel.update()
|
||||
layer_action_menu.left = e.global_x
|
||||
layer_action_menu.top = e.global_y
|
||||
layer_action_menu.show_menu()
|
||||
|
||||
def pickup_layer(e: ft.DragStartEvent):
|
||||
index = layer_panel.get_layer_index_from_position(e.local_y)
|
||||
if index >= len(layer_panel.layers):
|
||||
return
|
||||
layer_panel.layer_being_moved = layer_panel.layers[index]
|
||||
layer_panel.make_layer_active(layer_panel.layer_being_moved.index)
|
||||
layer_panel.update()
|
||||
|
||||
def on_layer_drag(e: ft.DragUpdateEvent):
|
||||
if not layer_panel.layer_being_moved:
|
||||
return
|
||||
index = layer_panel.get_layer_index_from_position(e.local_y)
|
||||
if index == layer_panel.layer_being_moved.index:
|
||||
return
|
||||
layer_panel.move_layer(layer_panel.layer_being_moved, index)
|
||||
|
||||
def drop_layer(e: ft.DragEndEvent):
|
||||
e.page.refresh_layers()
|
||||
layer_panel.layer_being_moved = None
|
||||
|
||||
|
||||
# LayerPanel == ft.Container
|
||||
layer_panel = LayerPanel(
|
||||
content = ft.GestureDetector(
|
||||
content = ft.Column(
|
||||
controls = [],
|
||||
alignment = 'start',
|
||||
expand = True,
|
||||
spacing = 0,
|
||||
scroll = 'hidden',
|
||||
),
|
||||
drag_interval = 10,
|
||||
on_tap_down = layer_left_click,
|
||||
on_secondary_tap_down = layer_right_click,
|
||||
on_vertical_drag_start = pickup_layer,
|
||||
on_vertical_drag_update = on_layer_drag,
|
||||
on_vertical_drag_end = drop_layer,
|
||||
),
|
||||
)
|
||||
|
||||
layer_panel.layers = []
|
||||
layer_panel.layer_being_moved = None
|
||||
layer_panel.layer_last_index = 0
|
||||
|
||||
|
||||
# AssetPanel == ft.Container
|
||||
asset_panel = AssetPanel(
|
||||
content = ft.Column(
|
||||
controls = [
|
||||
ft.Text("Under Construction"),
|
||||
],
|
||||
),
|
||||
)
|
||||
|
||||
def resize_asset_manager(e):
|
||||
asset_manager.resize_asset_manager(e)
|
||||
|
||||
def realign_canvas(e):
|
||||
e.page.align_canvas()
|
||||
|
||||
asset_manager_dragbar = ft.GestureDetector(
|
||||
mouse_cursor = ft.MouseCursor.RESIZE_COLUMN,
|
||||
drag_interval = 50,
|
||||
on_pan_update = resize_asset_manager,
|
||||
on_pan_end = realign_canvas,
|
||||
content = ft.VerticalDivider(),
|
||||
)
|
||||
|
||||
|
||||
# AssetManager == ft.Container
|
||||
asset_manager = AssetManager(
|
||||
content = ft.Row(
|
||||
controls = [
|
||||
ft.Column(
|
||||
controls = [
|
||||
ft.Tabs(
|
||||
selected_index = 0,
|
||||
animation_duration = 300,
|
||||
tabs = [
|
||||
ft.Tab(
|
||||
content = layer_panel,
|
||||
tab_content = ft.Text(
|
||||
value = "Layers",
|
||||
),
|
||||
),
|
||||
ft.Tab(
|
||||
content = asset_panel,
|
||||
tab_content = ft.Text(
|
||||
value = "Assets",
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
alignment = 'start',
|
||||
expand = True
|
||||
),
|
||||
asset_manager_dragbar,
|
||||
],
|
||||
expand = True,
|
||||
),
|
||||
clip_behavior = 'antiAlias',
|
||||
)
|
||||
|
||||
asset_manager.tabs = asset_manager.content.controls[0].controls[0].tabs
|
||||
asset_manager.layer_panel = layer_panel
|
||||
asset_manager.asset_panel = asset_panel
|
||||
asset_manager.dragbar = asset_manager_dragbar
|
||||
|
||||
'''
|
||||
# keep track of which layers are visible
|
||||
def show_hide_layer(self, e):
|
||||
parent = e.control.data['parent']
|
||||
if parent.data['visible']:
|
||||
parent.data['visible'] = False
|
||||
parent.opacity = 0.5
|
||||
e.control.icon = ft.icons.VISIBILITY_OFF
|
||||
else:
|
||||
parent.data['visible'] = True
|
||||
parent.opacity = 1.0
|
||||
e.control.icon = ft.icons.VISIBILITY
|
||||
self.update_visible_layer_list()
|
||||
parent.update()
|
||||
self.page.refresh_canvas()
|
||||
|
||||
def update_visible_layer_list(self):
|
||||
self.page.visible_layer_list = []
|
||||
layer_list = self.page.layer_list
|
||||
for layer in layer_list:
|
||||
if layer.data['type'] == 'slot':
|
||||
if layer.content.content.controls[1].data['visible']:
|
||||
self.page.visible_layer_list.append(layer)
|
||||
|
||||
# keep track of which layers are active
|
||||
def lock_unlock_layer(self, e):
|
||||
parent = e.control.data['parent']
|
||||
if parent.data['locked']:
|
||||
parent.data['locked'] = False
|
||||
e.control.icon = ft.icons.LOCK_OPEN_OUTLINED
|
||||
else:
|
||||
parent.data['locked'] = True
|
||||
e.control.icon = ft.icons.LOCK_OUTLINED
|
||||
self.update_active_layer_list()
|
||||
parent.update()
|
||||
|
||||
def update_active_layer_list(self):
|
||||
self.page.active_layer_list = []
|
||||
layer_list = self.page.layer_list
|
||||
for layer in layer_list:
|
||||
if layer.data['type'] == 'slot':
|
||||
if not layer.content.content.controls[1].data['locked']:
|
||||
self.page.active_layer_list.append(layer)
|
||||
|
||||
|
||||
'''
|
446
webui/flet/scripts/flet_canvas.py
Normal file
446
webui/flet/scripts/flet_canvas.py
Normal file
@ -0,0 +1,446 @@
|
||||
# flet_canvas.py
|
||||
|
||||
# Flet imports
|
||||
import flet as ft
|
||||
|
||||
# utils imports
|
||||
from scripts import flet_utils
|
||||
|
||||
|
||||
class Canvas(ft.Container):
|
||||
def setup(self):
|
||||
self.bgcolor = self.page.secondary_color
|
||||
self.padding = self.page.container_padding
|
||||
self.margin = self.page.container_margin
|
||||
|
||||
self.overlay.tools.center.icon_size = self.page.icon_size
|
||||
self.overlay.tools.zoom_in.icon_size = self.page.icon_size
|
||||
self.overlay.tools.zoom_out.icon_size = self.page.icon_size
|
||||
|
||||
self.overlay.size_display.content.color = self.page.text_color
|
||||
self.overlay.size_display.content.size = self.page.text_size
|
||||
self.add_canvas_background()
|
||||
self.center_canvas()
|
||||
self.refresh_canvas()
|
||||
|
||||
def on_page_change(self):
|
||||
self.bgcolor = self.page.secondary_color
|
||||
self.padding = self.page.container_padding
|
||||
self.margin = self.page.container_margin
|
||||
|
||||
self.overlay.tools.center.icon_size = self.page.icon_size
|
||||
self.overlay.tools.zoom_in.icon_size = self.page.icon_size
|
||||
self.overlay.tools.zoom_out.icon_size = self.page.icon_size
|
||||
|
||||
self.overlay.size_display.content.color = self.page.text_color
|
||||
self.overlay.size_display.content.size = self.page.text_size
|
||||
self.refresh_canvas()
|
||||
|
||||
def refresh_canvas(self):
|
||||
self.image_stack.refresh_stack()
|
||||
self.align_canvas()
|
||||
self.overlay.refresh_canvas_overlay()
|
||||
|
||||
def set_current_tool(self, tool):
|
||||
self.page.current_tool = tool
|
||||
|
||||
def add_canvas_background(self):
|
||||
self.image_stack.add_canvas_background()
|
||||
|
||||
def add_layer_image(self, image):
|
||||
return self.image_stack.add_layer_image(image)
|
||||
|
||||
def get_image_stack_preview(self):
|
||||
return self.image_stack.get_preview()
|
||||
|
||||
def center_canvas(self):
|
||||
width, height = self.page.get_viewport_size()
|
||||
self.image_stack.offset_x = 0
|
||||
self.image_stack.offset_y = 0
|
||||
self.image_stack.left = (width * 0.5) - (self.image_stack.width * 0.5)
|
||||
self.image_stack.top = (height * 0.5) - (self.image_stack.height * 0.5)
|
||||
self.overlay.frame.left = self.image_stack.left
|
||||
self.overlay.frame.top = self.image_stack.top
|
||||
self.update()
|
||||
|
||||
def align_canvas(self):
|
||||
width, height = self.page.get_viewport_size()
|
||||
self.image_stack.left = (width * 0.5) - (self.image_stack.width * 0.5) + self.image_stack.offset_x
|
||||
self.image_stack.top = (height * 0.5) - (self.image_stack.height * 0.5) + self.image_stack.offset_y
|
||||
self.overlay.frame.left = self.image_stack.left
|
||||
self.overlay.frame.top = self.image_stack.top
|
||||
self.overlay.frame.scale = self.image_stack.scale
|
||||
self.update()
|
||||
|
||||
def pan_canvas(self, e: ft.DragUpdateEvent):
|
||||
self.image_stack.offset_x += e.delta_x
|
||||
self.image_stack.offset_y += e.delta_y
|
||||
width, height = self.page.get_viewport_size()
|
||||
self.image_stack.offset_x = max(self.image_stack.offset_x, (width - self.image_stack.width) * 0.5)
|
||||
self.image_stack.offset_y = max(self.image_stack.offset_y, (height - self.image_stack.height) * 0.5)
|
||||
self.image_stack.offset_x = min(self.image_stack.offset_x, (self.image_stack.width - width) * 0.5)
|
||||
self.image_stack.offset_y = min(self.image_stack.offset_y, (self.image_stack.height - height) * 0.5)
|
||||
self.align_canvas()
|
||||
|
||||
def zoom_in(self, e):
|
||||
if self.image_stack.scale >= 4.0:
|
||||
self.image_stack.scale = 4.0
|
||||
else:
|
||||
self.image_stack.scale += 0.05
|
||||
self.image_stack.get_scaled_size()
|
||||
self.overlay.frame.scale = self.image_stack.scale
|
||||
self.align_canvas()
|
||||
|
||||
def zoom_out(self, e):
|
||||
if self.image_stack.scale <= 0.1:
|
||||
self.image_stack.scale = 0.1
|
||||
else:
|
||||
self.image_stack.scale -= 0.05
|
||||
self.overlay.frame.scale = self.image_stack.scale
|
||||
self.image_stack.get_scaled_size()
|
||||
self.align_canvas()
|
||||
|
||||
def clear_tools(self):
|
||||
self.overlay.clear_tools()
|
||||
|
||||
def set_current_tool(self, tool):
|
||||
if tool == 'pan':
|
||||
self.overlay.controls.pop(2)
|
||||
self.overlay.controls.insert(2,pan_tool)
|
||||
elif tool == 'move':
|
||||
self.overlay.controls.pop(2)
|
||||
self.overlay.controls.insert(2,move_tool)
|
||||
elif tool == 'box_select':
|
||||
self.overlay.controls.pop(2)
|
||||
self.overlay.controls.insert(2,box_select_tool)
|
||||
elif tool == 'brush':
|
||||
self.overlay.controls.pop(2)
|
||||
self.overlay.controls.insert(2,brush_tool)
|
||||
elif tool == 'fill':
|
||||
self.overlay.controls.pop(2)
|
||||
self.overlay.controls.insert(2,fill_tool)
|
||||
else:
|
||||
pass
|
||||
self.update()
|
||||
|
||||
|
||||
class ImageStack(ft.Container):
|
||||
def add_canvas_background(self):
|
||||
image = self.page.canvas_background
|
||||
canvas_bg = LayerImage(
|
||||
left = 0,
|
||||
top = 0,
|
||||
width = self.width,
|
||||
height = self.height,
|
||||
content = ft.Image(
|
||||
src_base64 = flet_utils.convert_image_to_base64(image),
|
||||
width = 256,
|
||||
height = 256,
|
||||
repeat = 'repeat',
|
||||
gapless_playback = True,
|
||||
),
|
||||
)
|
||||
canvas_bg.image = image
|
||||
canvas_bg.offset_x = 0
|
||||
canvas_bg.offset_y = 0
|
||||
self.canvas_bg = canvas_bg
|
||||
self.content.controls.append(canvas_bg)
|
||||
|
||||
def add_layer_image(self, image):
|
||||
layer_image = None
|
||||
if image.path == None:
|
||||
layer_image = LayerImage(
|
||||
left = 0,
|
||||
top = 0,
|
||||
width = image.width,
|
||||
height = image.height,
|
||||
content = ft.Image(
|
||||
src_base64 = flet_utils.convert_image_to_base64(image),
|
||||
width = image.width,
|
||||
height = image.height,
|
||||
gapless_playback = True,
|
||||
),
|
||||
)
|
||||
else:
|
||||
layer_image = LayerImage(
|
||||
left = 0,
|
||||
top = 0,
|
||||
width = image.width,
|
||||
height = image.height,
|
||||
content = ft.Image(
|
||||
src = f'{image.path}',
|
||||
width = image.width,
|
||||
height = image.height,
|
||||
gapless_playback = True,
|
||||
),
|
||||
)
|
||||
layer_image.image = image
|
||||
layer_image.offset_x = 0
|
||||
layer_image.offset_y = 0
|
||||
self.center_layer(layer_image)
|
||||
self.content.controls.append(layer_image)
|
||||
return layer_image
|
||||
|
||||
def get_preview(self):
|
||||
stack = self.content.controls
|
||||
return flet_utils.get_preview_from_stack(self.page.canvas_size, stack)
|
||||
|
||||
def refresh_stack(self):
|
||||
self.content.controls.clear()
|
||||
for slot in self.page.visible_layers:
|
||||
self.content.controls.insert(0, slot.layer_image)
|
||||
self.content.controls.insert(0, self.canvas_bg)
|
||||
self.update()
|
||||
|
||||
def get_scaled_size(self):
|
||||
self.scaled_width = self.width * self.scale
|
||||
self.scaled_height = self.height * self.scale
|
||||
|
||||
def center_layer(self, layer_image):
|
||||
layer_image.offset_x = 0
|
||||
layer_image.offset_y = 0
|
||||
layer_image.left = (self.width * 0.5) - (layer_image.width * 0.5)
|
||||
layer_image.top = (self.height * 0.5) - (layer_image.height * 0.5)
|
||||
|
||||
def align_layer(self, layer_image):
|
||||
layer_image.left = ((self.width - layer_image.width) * 0.5) + layer_image.offset_x
|
||||
layer_image.top = ((self.height - layer_image.height) * 0.5) + layer_image.offset_y
|
||||
|
||||
def move_layer(self, e: ft.DragUpdateEvent):
|
||||
layer = self.page.active_layer.layer_image
|
||||
layer.offset_x += e.delta_x
|
||||
layer.offset_y += e.delta_y
|
||||
self.align_layer(layer)
|
||||
self.update()
|
||||
|
||||
def finish_move_layer(self, e: ft.DragEndEvent):
|
||||
canvas.refresh_canvas()
|
||||
|
||||
def resize_layer(self, e: ft.DragUpdateEvent):
|
||||
pass
|
||||
|
||||
def box_select(self, e):
|
||||
pass
|
||||
|
||||
def bucket_fill(self, e):
|
||||
pass
|
||||
|
||||
|
||||
class LayerImage(ft.Container):
|
||||
pass
|
||||
|
||||
|
||||
class CanvasGestures(ft.GestureDetector):
|
||||
pass
|
||||
|
||||
|
||||
class CanvasOverlay(ft.Stack):
|
||||
def refresh_canvas_overlay(self):
|
||||
self.refresh_canvas_size_display()
|
||||
self.page.refresh_canvas_preview()
|
||||
|
||||
def refresh_canvas_size_display(self):
|
||||
self.size_display.content.value = str(self.page.canvas_size)
|
||||
self.update()
|
||||
|
||||
def clear_tools(self):
|
||||
for tool in canvas_tools.content.controls:
|
||||
tool.selected = False
|
||||
|
||||
|
||||
# ImageStack == ft.Container
|
||||
image_stack = ImageStack(
|
||||
width = 4096,
|
||||
height = 4096,
|
||||
left = 0,
|
||||
top = 0,
|
||||
scale = 1.0,
|
||||
content = ft.Stack(),
|
||||
)
|
||||
|
||||
image_stack.offset_x = 0
|
||||
image_stack.offset_y = 0
|
||||
image_stack.scaled_width = image_stack.width
|
||||
image_stack.scaled_height = image_stack.height
|
||||
|
||||
canvas_frame = ft.Container(
|
||||
width = 4096,
|
||||
height = 4096,
|
||||
top = 0,
|
||||
left = 0,
|
||||
scale = 1.0,
|
||||
image_fit = 'cover',
|
||||
alignment = ft.alignment.center,
|
||||
content = ft.Image(
|
||||
src_base64 = flet_utils.get_canvas_frame((512,512)),
|
||||
gapless_playback = True,
|
||||
),
|
||||
)
|
||||
|
||||
# CanvasGestures == ft.GestureDetector
|
||||
def pan_canvas(e):
|
||||
canvas.pan_canvas(e)
|
||||
|
||||
pan_tool = CanvasGestures(
|
||||
mouse_cursor = ft.MouseCursor.GRAB,
|
||||
drag_interval = 10,
|
||||
on_pan_update = pan_canvas,
|
||||
)
|
||||
|
||||
def select_layer(e):
|
||||
pass
|
||||
|
||||
def move_layer(e):
|
||||
image_stack.move_layer(e)
|
||||
|
||||
def finish_move_layer(e):
|
||||
image_stack.finish_move_layer(e)
|
||||
|
||||
move_tool = CanvasGestures(
|
||||
mouse_cursor = ft.MouseCursor.MOVE,
|
||||
drag_interval = 10,
|
||||
on_pan_start = select_layer,
|
||||
on_pan_update = move_layer,
|
||||
on_pan_end = finish_move_layer,
|
||||
)
|
||||
|
||||
def set_select_start(e):
|
||||
pass
|
||||
|
||||
def draw_select_box(e):
|
||||
pass
|
||||
|
||||
def get_box_select(e):
|
||||
pass
|
||||
|
||||
box_select_tool = CanvasGestures(
|
||||
mouse_cursor = ft.MouseCursor.GRAB,
|
||||
drag_interval = 10,
|
||||
on_pan_start = set_select_start,
|
||||
on_pan_update = draw_select_box,
|
||||
on_pan_end = get_box_select,
|
||||
)
|
||||
|
||||
def draw_on_layer(e):
|
||||
pass
|
||||
|
||||
brush_tool = CanvasGestures(
|
||||
mouse_cursor = ft.MouseCursor.GRAB,
|
||||
drag_interval = 10,
|
||||
on_pan_update = draw_on_layer,
|
||||
)
|
||||
|
||||
def fill_selection(e):
|
||||
pass
|
||||
|
||||
fill_tool = CanvasGestures(
|
||||
mouse_cursor = ft.MouseCursor.GRAB,
|
||||
drag_interval = 10,
|
||||
on_tap = fill_selection,
|
||||
)
|
||||
|
||||
canvas_size_display = ft.Container(
|
||||
content = ft.Text(
|
||||
value = "test",
|
||||
),
|
||||
left = 4,
|
||||
bottom = 4,
|
||||
padding = 4,
|
||||
border_radius = 10,
|
||||
opacity = 0.5,
|
||||
bgcolor = 'black',
|
||||
)
|
||||
|
||||
def center_canvas(e):
|
||||
canvas.center_canvas()
|
||||
|
||||
center_canvas_button = ft.IconButton(
|
||||
content = ft.Icon(ft.icons.FILTER_CENTER_FOCUS_OUTLINED),
|
||||
tooltip = 'center canvas',
|
||||
on_click = center_canvas,
|
||||
)
|
||||
|
||||
def set_pan_tool(e):
|
||||
e.page.set_current_tool(e)
|
||||
|
||||
pan_canvas_button = ft.IconButton(
|
||||
content = ft.Icon(ft.icons.PAN_TOOL_OUTLINED),
|
||||
tooltip = 'pan canvas',
|
||||
on_click = set_pan_tool,
|
||||
selected = True,
|
||||
data = {'label':'pan'},
|
||||
)
|
||||
|
||||
def zoom_in_canvas(e):
|
||||
canvas.zoom_in(e)
|
||||
|
||||
zoom_in_button = ft.IconButton(
|
||||
content = ft.Icon(ft.icons.ZOOM_IN_OUTLINED),
|
||||
tooltip = 'zoom in canvas',
|
||||
on_click = zoom_in_canvas,
|
||||
)
|
||||
|
||||
def zoom_out_canvas(e):
|
||||
canvas.zoom_out(e)
|
||||
|
||||
zoom_out_button = ft.IconButton(
|
||||
content = ft.Icon(ft.icons.ZOOM_OUT_OUTLINED),
|
||||
tooltip = 'zoom out canvas',
|
||||
on_click = zoom_out_canvas,
|
||||
)
|
||||
|
||||
canvas_tools = ft.Container(
|
||||
content = ft.Column(
|
||||
controls = [
|
||||
center_canvas_button,
|
||||
pan_canvas_button,
|
||||
zoom_in_button,
|
||||
zoom_out_button,
|
||||
],
|
||||
horizontal_alignment = 'end',
|
||||
),
|
||||
top = 4,
|
||||
right = 4,
|
||||
padding = 4,
|
||||
border_radius = 10,
|
||||
opacity = 0.5,
|
||||
bgcolor = 'black',
|
||||
disabled = False,
|
||||
)
|
||||
|
||||
canvas_tools.center = center_canvas_button
|
||||
canvas_tools.zoom_in = zoom_in_button
|
||||
canvas_tools.zoom_out = zoom_out_button
|
||||
|
||||
|
||||
# CanvasOverlay == ft.Stack
|
||||
canvas_overlay = CanvasOverlay(
|
||||
[
|
||||
canvas_frame,
|
||||
pan_tool,
|
||||
canvas_size_display,
|
||||
canvas_tools,
|
||||
],
|
||||
)
|
||||
|
||||
canvas_overlay.frame = canvas_frame
|
||||
canvas_overlay.size_display = canvas_size_display
|
||||
canvas_overlay.tools = canvas_tools
|
||||
|
||||
|
||||
# Canvas = ft.Container
|
||||
canvas = Canvas(
|
||||
content = ft.Stack(
|
||||
[
|
||||
image_stack,
|
||||
canvas_overlay,
|
||||
],
|
||||
),
|
||||
clip_behavior = 'antiAlias',
|
||||
alignment = ft.alignment.center,
|
||||
expand = True,
|
||||
)
|
||||
|
||||
canvas.image_stack = image_stack
|
||||
canvas.overlay = canvas_overlay
|
||||
|
140
webui/flet/scripts/flet_file_manager.py
Normal file
140
webui/flet/scripts/flet_file_manager.py
Normal file
@ -0,0 +1,140 @@
|
||||
# flet_file_manager.py
|
||||
|
||||
# Flet imports
|
||||
import flet as ft
|
||||
|
||||
# other imports
|
||||
from math import pi
|
||||
from typing import Dict
|
||||
from loguru import logger
|
||||
|
||||
# utils imports
|
||||
from scripts import flet_utils
|
||||
|
||||
|
||||
class UploadWindow(ft.AlertDialog):
|
||||
def upload_file(self, e):
|
||||
if file_picker.result is not None and file_picker.result.files is not None:
|
||||
file_list = []
|
||||
for f in file_picker.result.files:
|
||||
upload_url = e.page.get_upload_url(f.name, 600)
|
||||
img = ft.FilePickerUploadFile(f.name,upload_url)
|
||||
file_list.append(img)
|
||||
file_picker.upload(file_list)
|
||||
|
||||
def upload_complete(self, e):
|
||||
self.progress_bars.clear()
|
||||
self.selected_files.controls.clear()
|
||||
e.page.close_uploads(e)
|
||||
e.page.add_images_as_layers(file_picker.images)
|
||||
file_picker.images.clear()
|
||||
e.page.message('File upload(s) complete.')
|
||||
|
||||
def get_image_from_uploads(self, name):
|
||||
return flet_utils.get_image_from_uploads(name)
|
||||
|
||||
def get_file_display(self, name, progress):
|
||||
display = ft.Row(
|
||||
controls = [
|
||||
progress,
|
||||
ft.Text(name),
|
||||
],
|
||||
)
|
||||
return display
|
||||
|
||||
class ImportWindow(ft.AlertDialog):
|
||||
pass
|
||||
|
||||
|
||||
selected_files = ft.Column(
|
||||
scroll = 'auto',
|
||||
tight = True,
|
||||
controls = [],
|
||||
);
|
||||
|
||||
progress_bars: Dict[str, ft.ProgressBar] = {}
|
||||
|
||||
def upload_file(e):
|
||||
uploads.upload_file(e)
|
||||
|
||||
def close_upload_window(e):
|
||||
e.page.close_uploads(e)
|
||||
|
||||
uploads = UploadWindow(
|
||||
title = ft.Text("Confirm file upload(s)"),
|
||||
content = selected_files,
|
||||
actions_alignment = "center",
|
||||
actions = [
|
||||
ft.ElevatedButton("UPLOAD", on_click = upload_file),
|
||||
ft.TextButton("CANCEL", on_click = close_upload_window),
|
||||
],
|
||||
)
|
||||
|
||||
uploads.selected_files = selected_files
|
||||
uploads.progress_bars = progress_bars
|
||||
|
||||
def import_file(e):
|
||||
e.page.close_imports(e)
|
||||
|
||||
def close_import_window(e):
|
||||
e.page.close_imports(e)
|
||||
|
||||
imports = ImportWindow(
|
||||
title = ft.Text("Confirm file import(s)"),
|
||||
content = selected_files,
|
||||
actions_alignment = "center",
|
||||
actions = [
|
||||
ft.ElevatedButton("IMPORT", on_click = import_file),
|
||||
ft.TextButton("CANCEL", on_click = close_import_window),
|
||||
],
|
||||
)
|
||||
|
||||
imports.selected_files = selected_files
|
||||
imports.progress_bars = progress_bars
|
||||
|
||||
def pick_images(e: ft.FilePickerResultEvent):
|
||||
progress_bars.clear()
|
||||
selected_files.controls.clear()
|
||||
file_picker.images.clear()
|
||||
# check to see if files or directory were chosen
|
||||
if e.files is not None and e.path is None:
|
||||
for f in e.files:
|
||||
prog = ft.ProgressRing(
|
||||
width = 12,
|
||||
height = 12,
|
||||
stroke_width = 2,
|
||||
value = 0,
|
||||
color = 'blue',
|
||||
)
|
||||
progress_bars[f.name] = prog
|
||||
selected_files.controls.append(uploads.get_file_display(f.name,prog))
|
||||
file_picker.pending += 1
|
||||
# upload if remote, import if local
|
||||
if e.page.web:
|
||||
e.page.open_uploads(e)
|
||||
else:
|
||||
e.page.open_imports(e)
|
||||
|
||||
def on_image_upload(e: ft.FilePickerUploadEvent):
|
||||
if e.error:
|
||||
e.page.message(f"Upload error occurred! Failed to fetch '{e.file_name}'.",1)
|
||||
file_picker.pending -= 1
|
||||
else:
|
||||
# update progress bars
|
||||
progress_bars[e.file_name].value = e.progress
|
||||
progress_bars[e.file_name].update()
|
||||
if e.progress >= 1:
|
||||
file_picker.pending -= 1
|
||||
file_picker.images.append(uploads.get_image_from_uploads(e.file_name))
|
||||
if file_picker.pending <= 0:
|
||||
file_picker.pending = 0
|
||||
uploads.upload_complete(e)
|
||||
|
||||
file_picker = ft.FilePicker(
|
||||
on_result = pick_images,
|
||||
on_upload = on_image_upload
|
||||
)
|
||||
|
||||
file_picker.pending = 0
|
||||
file_picker.images = []
|
||||
|
152
webui/flet/scripts/flet_gallery_window.py
Normal file
152
webui/flet/scripts/flet_gallery_window.py
Normal file
@ -0,0 +1,152 @@
|
||||
# flet_gallery_window.py
|
||||
|
||||
# Flet imports
|
||||
import flet as ft
|
||||
|
||||
# utils imports
|
||||
from scripts import flet_utils
|
||||
|
||||
|
||||
class GalleryWindow(ft.AlertDialog):
|
||||
def setup(self):
|
||||
self.refresh_galleries()
|
||||
|
||||
def refresh_galleries(self):
|
||||
self.refresh_gallery('uploads')
|
||||
self.refresh_gallery('outputs')
|
||||
|
||||
def refresh_gallery(self, gallery_name):
|
||||
index = None
|
||||
if gallery_name == 'uploads':
|
||||
self.uploads_gallery.get_gallery_display(gallery_name)
|
||||
elif gallery_name == 'outputs':
|
||||
self.outputs_gallery.get_gallery_display(gallery_name)
|
||||
else:
|
||||
page.message(f'{gallery_name} gallery not found.', 1)
|
||||
return None
|
||||
|
||||
def get_gallery_images(self, gallery_name):
|
||||
return flet_utils.get_gallery_images(gallery_name)
|
||||
|
||||
def select_image(self, e):
|
||||
if e.control.border :
|
||||
e.control.border = None
|
||||
if e.control.image in self.selected_images:
|
||||
self.selected_images.remove(e.control.image)
|
||||
e.control.update()
|
||||
else:
|
||||
e.control.border = ft.border.all(2, e.page.tertiary_color)
|
||||
self.selected_images.append(e.control.image)
|
||||
e.control.update()
|
||||
|
||||
class GalleryDisplay(ft.Container):
|
||||
def get_gallery_display(self, gallery_name):
|
||||
self.content = ft.GridView(
|
||||
controls = None,
|
||||
padding = 0,
|
||||
runs_count = 3,
|
||||
run_spacing = 12,
|
||||
spacing = 12,
|
||||
expand = True,
|
||||
)
|
||||
gallery = gallery_window.get_gallery_images(gallery_name)
|
||||
if not gallery:
|
||||
self.content.controls.append(
|
||||
ft.Image(
|
||||
src = '/images/chickens.jpg',
|
||||
tooltip = 'Nothing here but us chickens!',
|
||||
gapless_playback = True,
|
||||
)
|
||||
)
|
||||
return
|
||||
|
||||
for image in gallery:
|
||||
gallery_image = GalleryImage(
|
||||
content = ft.Image(
|
||||
src = image.path,
|
||||
tooltip = image.filename,
|
||||
width = image.width,
|
||||
height = image.height,
|
||||
gapless_playback = True,
|
||||
),
|
||||
image_fit = 'contain',
|
||||
height = image.height,
|
||||
width = image.width,
|
||||
padding = 0,
|
||||
margin = 0,
|
||||
border = None,
|
||||
on_click = gallery_window.select_image
|
||||
)
|
||||
gallery_image.image = image
|
||||
self.content.controls.append(gallery_image)
|
||||
|
||||
class GalleryImage(ft.Container):
|
||||
pass
|
||||
|
||||
def add_as_new_layer(e):
|
||||
if gallery_window.selected_images:
|
||||
e.page.add_images_as_layers(gallery_window.selected_images)
|
||||
gallery_window.selected_images.clear()
|
||||
for tab in gallery_window.content.content.tabs:
|
||||
for image in tab.content.content.controls:
|
||||
image.border = None
|
||||
image.update()
|
||||
|
||||
def save_to_disk(e):
|
||||
pass
|
||||
|
||||
def remove_from_gallery(e):
|
||||
pass
|
||||
|
||||
uploads_gallery = GalleryDisplay(
|
||||
content = None,
|
||||
clip_behavior = 'antiAlias',
|
||||
)
|
||||
|
||||
outputs_gallery = GalleryDisplay(
|
||||
content = None,
|
||||
clip_behavior = 'antiAlias',
|
||||
)
|
||||
|
||||
# GalleryWindow == ft.AlertDialog
|
||||
gallery_window = GalleryWindow(
|
||||
title = ft.Text('Gallery'),
|
||||
content = ft.Container(
|
||||
content = ft.Tabs(
|
||||
selected_index = 0,
|
||||
animation_duration = 300,
|
||||
tabs = [
|
||||
ft.Tab(
|
||||
text = "Uploads",
|
||||
content = uploads_gallery,
|
||||
),
|
||||
ft.Tab(
|
||||
text = "Outputs",
|
||||
content = outputs_gallery,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
actions = [
|
||||
ft.ElevatedButton(
|
||||
text = "Add As New Layer(s)",
|
||||
icon = ft.icons.ADD_OUTLINED,
|
||||
on_click = add_as_new_layer,
|
||||
),
|
||||
ft.ElevatedButton(
|
||||
text = "Save",
|
||||
icon = ft.icons.SAVE_OUTLINED,
|
||||
on_click = save_to_disk,
|
||||
),
|
||||
ft.ElevatedButton(
|
||||
text = "Discard",
|
||||
icon = ft.icons.DELETE_OUTLINED,
|
||||
on_click = remove_from_gallery,
|
||||
),
|
||||
],
|
||||
actions_alignment="end",
|
||||
)
|
||||
|
||||
gallery_window.uploads_gallery = uploads_gallery
|
||||
gallery_window.outputs_gallery = outputs_gallery
|
||||
gallery_window.selected_images = []
|
142
webui/flet/scripts/flet_messages.py
Normal file
142
webui/flet/scripts/flet_messages.py
Normal file
@ -0,0 +1,142 @@
|
||||
# flet_messages.py
|
||||
|
||||
# Flet imports
|
||||
import flet as ft
|
||||
|
||||
# utils imports
|
||||
from scripts import flet_utils
|
||||
|
||||
|
||||
class Messages(ft.Container):
|
||||
def setup(self):
|
||||
self.height = self.page.bottom_panel_height
|
||||
self.bgcolor = self.page.primary_color
|
||||
self.padding = self.page.container_padding
|
||||
self.margin = self.page.container_margin
|
||||
|
||||
self.set_tab_text_size(self.page.text_size)
|
||||
self.set_tab_bgcolor(self.page.secondary_color)
|
||||
self.set_tab_padding(self.page.container_padding)
|
||||
self.set_tab_margin(self.page.container_margin)
|
||||
|
||||
self.dragbar.content.height = self.page.divider_height
|
||||
self.dragbar.content.color = self.page.tertiary_color
|
||||
|
||||
def on_page_change(self):
|
||||
self.height = self.page.bottom_panel_height
|
||||
self.bgcolor = self.page.primary_color
|
||||
self.padding = self.page.container_padding
|
||||
self.margin = self.page.container_margin
|
||||
|
||||
self.set_tab_text_size(self.page.text_size)
|
||||
self.set_tab_bgcolor(self.page.secondary_color)
|
||||
self.set_tab_padding(self.page.container_padding)
|
||||
self.set_tab_margin(self.page.container_margin)
|
||||
|
||||
self.dragbar.content.height = self.page.divider_height
|
||||
self.dragbar.content.color = self.page.tertiary_color
|
||||
|
||||
|
||||
def set_tab_text_size(self, size):
|
||||
for tab in self.tabs:
|
||||
tab.tab_content.size = size
|
||||
|
||||
def set_tab_bgcolor(self, color):
|
||||
for tab in self.tabs:
|
||||
tab.content.bgcolor = color
|
||||
|
||||
def set_tab_padding(self, padding):
|
||||
for tab in self.tabs:
|
||||
tab.content.padding = padding
|
||||
|
||||
def set_tab_margin(self, margin):
|
||||
for tab in self.tabs:
|
||||
tab.content.margin = margin
|
||||
|
||||
def resize_messages(self, e: ft.DragUpdateEvent):
|
||||
self.page.bottom_panel_height = max(100, self.page.bottom_panel_height - e.delta_y)
|
||||
self.height = self.page.bottom_panel_height
|
||||
self.page.update()
|
||||
|
||||
def message(self, text, err = 0):
|
||||
if err:
|
||||
text = "ERROR: " + text
|
||||
self.add_message_to_messages(err,text)
|
||||
flet_utils.log_message(text)
|
||||
|
||||
def prune_messages(self):
|
||||
if len(message_list.controls) > self.page.max_message_history:
|
||||
message_list.controls.pop(0)
|
||||
message_list.update()
|
||||
|
||||
def add_message_to_messages(self,err,text):
|
||||
if err:
|
||||
msg = ft.Text(value = text, color = ft.colors.RED)
|
||||
else:
|
||||
msg = ft.Text(value = text)
|
||||
message_list.controls.append(msg)
|
||||
self.prune_messages()
|
||||
|
||||
|
||||
message_list = ft.ListView(
|
||||
spacing = 4,
|
||||
auto_scroll = True,
|
||||
controls = [],
|
||||
)
|
||||
|
||||
messages_panel = ft.Container(
|
||||
content = message_list,
|
||||
)
|
||||
|
||||
video_editor_panel = ft.Column(
|
||||
expand = True,
|
||||
controls = [ft.Text("Under Construction")]
|
||||
)
|
||||
|
||||
def resize_messages(e):
|
||||
messages.resize_messages(e)
|
||||
|
||||
def realign_canvas(e):
|
||||
e.page.align_canvas()
|
||||
|
||||
messages_dragbar = ft.GestureDetector(
|
||||
mouse_cursor = ft.MouseCursor.RESIZE_ROW,
|
||||
drag_interval = 50,
|
||||
on_pan_update = resize_messages,
|
||||
on_pan_end = realign_canvas,
|
||||
content = ft.Divider(),
|
||||
)
|
||||
|
||||
messages = Messages(
|
||||
content = ft.Stack(
|
||||
controls = [
|
||||
messages_dragbar,
|
||||
ft.Tabs(
|
||||
selected_index = 0,
|
||||
animation_duration = 300,
|
||||
tabs = [
|
||||
ft.Tab(
|
||||
content = messages_panel,
|
||||
tab_content = ft.Text(
|
||||
value = 'Messages',
|
||||
),
|
||||
),
|
||||
ft.Tab(
|
||||
content = video_editor_panel,
|
||||
tab_content = ft.Text(
|
||||
value = 'Video Editor',
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
clip_behavior = 'antiAlias',
|
||||
)
|
||||
|
||||
messages.dragbar = messages_dragbar
|
||||
messages.tabs = messages.content.controls[1].tabs
|
||||
messages.messages_panel = messages_panel
|
||||
messages.video_editor_panel = video_editor_panel
|
||||
messages.message_list = message_list
|
||||
|
365
webui/flet/scripts/flet_property_manager.py
Normal file
365
webui/flet/scripts/flet_property_manager.py
Normal file
@ -0,0 +1,365 @@
|
||||
# flet_property_manager.py
|
||||
|
||||
# Flet imports
|
||||
import flet as ft
|
||||
|
||||
# utils imports
|
||||
from scripts import flet_utils
|
||||
|
||||
|
||||
class PropertyManager(ft.Container):
|
||||
def setup(self):
|
||||
self.width = self.page.right_panel_width
|
||||
self.bgcolor = self.page.primary_color
|
||||
self.padding = self.page.container_padding
|
||||
self.margin = self.page.container_margin
|
||||
self.set_tab_text_size(self.page.text_size)
|
||||
self.set_tab_bgcolor(self.page.secondary_color)
|
||||
self.set_tab_padding(self.page.container_padding)
|
||||
self.set_tab_margin(self.page.container_margin)
|
||||
self.dragbar.content.width = self.page.vertical_divider_width
|
||||
self.dragbar.content.color = self.page.tertiary_color
|
||||
self.property_panel.preview.width = self.page.right_panel_width
|
||||
self.property_panel.preview_dragbar.content.content.height = self.page.divider_height
|
||||
self.property_panel.preview_dragbar.content.content.color = self.page.tertiary_color
|
||||
self.property_panel.canvas_properties_divider.content.height = self.page.divider_height
|
||||
self.property_panel.canvas_properties_divider.content.color = self.page.tertiary_color
|
||||
self.property_panel.layer_properties_divider.content.height = self.page.divider_height
|
||||
self.property_panel.layer_properties_divider.content.color = self.page.tertiary_color
|
||||
|
||||
self.page.refresh_canvas_preview()
|
||||
self.refresh_canvas_properties()
|
||||
|
||||
def on_page_change(self):
|
||||
self.width = self.page.right_panel_width
|
||||
self.bgcolor = self.page.primary_color
|
||||
self.padding = self.page.container_padding
|
||||
self.margin = self.page.container_margin
|
||||
self.set_tab_text_size(self.page.text_size)
|
||||
self.set_tab_bgcolor(self.page.secondary_color)
|
||||
self.set_tab_padding(self.page.container_padding)
|
||||
self.set_tab_margin(self.page.container_margin)
|
||||
self.dragbar.content.width = self.page.vertical_divider_width
|
||||
self.dragbar.content.color = self.page.tertiary_color
|
||||
self.property_panel.preview.width = self.page.right_panel_width
|
||||
self.property_panel.preview_dragbar.content.content.height = self.page.divider_height
|
||||
self.property_panel.preview_dragbar.content.content.color = self.page.tertiary_color
|
||||
self.property_panel.canvas_properties_divider.content.height = self.page.divider_height
|
||||
self.property_panel.canvas_properties_divider.content.color = self.page.tertiary_color
|
||||
self.property_panel.layer_properties_divider.content.height = self.page.divider_height
|
||||
self.property_panel.layer_properties_divider.content.color = self.page.tertiary_color
|
||||
|
||||
def set_tab_text_size(self, size):
|
||||
for tab in self.tabs:
|
||||
tab.tab_content.size = size
|
||||
|
||||
def set_tab_bgcolor(self, color):
|
||||
for tab in self.tabs:
|
||||
tab.content.content.bgcolor = color
|
||||
|
||||
def set_tab_padding(self, padding):
|
||||
for tab in self.tabs:
|
||||
tab.content.padding = padding
|
||||
|
||||
def set_tab_margin(self, margin):
|
||||
for tab in self.tabs:
|
||||
tab.content.margin = margin
|
||||
|
||||
def set_preview_size(self, width):
|
||||
self.property_panel.preview.width = width
|
||||
|
||||
def set_preview_image(self, image):
|
||||
self.property_panel.preview.content.src_base64 = flet_utils.convert_image_to_base64(image)
|
||||
self.property_panel.update()
|
||||
|
||||
def refresh_canvas_properties(self):
|
||||
self.property_panel.refresh_canvas_properties()
|
||||
|
||||
def refresh_layer_properties(self):
|
||||
self.property_panel.refresh_layer_properties()
|
||||
|
||||
def resize_property_manager(self, e: ft.DragUpdateEvent):
|
||||
self.page.right_panel_width = max(250, self.page.right_panel_width - e.delta_x)
|
||||
self.width = self.page.right_panel_width
|
||||
self.property_panel.preview.width = self.page.right_panel_width
|
||||
self.page.update()
|
||||
|
||||
|
||||
class PropertyPanel(ft.Container):
|
||||
def resize_preview(self, e):
|
||||
self.preview.height = max(200, self.preview.height + e.delta_y)
|
||||
self.update()
|
||||
|
||||
def refresh_canvas_properties(self):
|
||||
self.canvas_properties.controls[0].controls[1].value = self.page.canvas_size[0]
|
||||
self.canvas_properties.controls[0].controls[3].value = self.page.canvas_size[1]
|
||||
self.canvas_properties.update()
|
||||
|
||||
def refresh_layer_properties(self):
|
||||
active = True if self.page.active_layer else False
|
||||
if active:
|
||||
self.layer_property_header.disabled = False
|
||||
self.layer_property_header.open = True
|
||||
self.layer_property_header.icon = ft.icons.ARROW_DROP_DOWN
|
||||
self.layer_property_header.icon_color = self.page.tertiary_color
|
||||
self.layer_properties.visible = True
|
||||
self.layer_properties.controls[0].controls[0].value = self.page.active_layer.label.value
|
||||
self.layer_properties.controls[1].controls[0].value = self.page.active_layer.image.width
|
||||
self.layer_properties.controls[1].controls[1].value = self.page.active_layer.image.height
|
||||
else:
|
||||
self.layer_property_header.disabled = True
|
||||
self.layer_property_header.open = False
|
||||
self.layer_property_header.icon = ft.icons.ARROW_RIGHT
|
||||
self.layer_property_header.icon_color = None
|
||||
self.layer_properties.visible = False
|
||||
self.update()
|
||||
|
||||
preview_pane = ft.Container(
|
||||
content = ft.Image(
|
||||
src_base64 = None,
|
||||
gapless_playback = True,
|
||||
),
|
||||
image_fit = 'contain',
|
||||
bgcolor = 'black',
|
||||
height = 200,
|
||||
padding = 0,
|
||||
margin = 0,
|
||||
)
|
||||
|
||||
def resize_preview(e):
|
||||
property_panel.resize_preview(e)
|
||||
|
||||
preview_dragbar = ft.GestureDetector(
|
||||
mouse_cursor = ft.MouseCursor.RESIZE_ROW,
|
||||
drag_interval = 50,
|
||||
on_pan_update = resize_preview,
|
||||
content = ft.Container(
|
||||
content = ft.Divider(),
|
||||
margin = 0,
|
||||
padding = 0,
|
||||
),
|
||||
)
|
||||
|
||||
def open_close_canvas_properties(e):
|
||||
if canvas_property_header.open:
|
||||
e.control.icon = ft.icons.ARROW_RIGHT
|
||||
e.control.icon_color = None
|
||||
canvas_property_header.open = False
|
||||
canvas_properties.visible = False
|
||||
property_panel.update()
|
||||
else:
|
||||
e.control.icon = ft.icons.ARROW_DROP_DOWN
|
||||
e.control.icon_color = e.page.tertiary_color
|
||||
canvas_property_header.open = True
|
||||
canvas_properties.visible = True
|
||||
property_panel.update()
|
||||
|
||||
canvas_property_header = ft.Column(
|
||||
controls = [
|
||||
ft.TextButton(
|
||||
text = "Canvas Properties",
|
||||
icon = ft.icons.ARROW_RIGHT,
|
||||
on_click = open_close_canvas_properties,
|
||||
),
|
||||
],
|
||||
)
|
||||
|
||||
canvas_property_header.open = False
|
||||
|
||||
canvas_properties = ft.Column(
|
||||
visible = False,
|
||||
controls = [
|
||||
ft.Row(
|
||||
controls = [
|
||||
ft.Text(
|
||||
value = 'Width:',
|
||||
text_align = 'center',
|
||||
no_wrap = True,
|
||||
expand = 2,
|
||||
),
|
||||
ft.Text(
|
||||
value = 0,
|
||||
text_align = 'start',
|
||||
expand = 1,
|
||||
),
|
||||
ft.Text(
|
||||
value = 'Height:',
|
||||
text_align = 'start',
|
||||
no_wrap = True,
|
||||
expand = 2,
|
||||
),
|
||||
ft.Text(
|
||||
value = 0,
|
||||
text_align = 'center',
|
||||
expand = 1,
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
)
|
||||
|
||||
canvas_property_divider = ft.Container(
|
||||
content = ft.Divider(),
|
||||
margin = 0,
|
||||
padding = 0,
|
||||
)
|
||||
|
||||
def open_close_layer_properties(e):
|
||||
if layer_property_header.open:
|
||||
e.control.icon = ft.icons.ARROW_RIGHT
|
||||
e.control.icon_color = None
|
||||
layer_property_header.open = False
|
||||
layer_properties.visible = False
|
||||
property_panel.update()
|
||||
else:
|
||||
e.control.icon = ft.icons.ARROW_DROP_DOWN
|
||||
e.control.icon_color = e.page.tertiary_color
|
||||
layer_property_header.open = True
|
||||
layer_properties.visible = True
|
||||
property_panel.update()
|
||||
|
||||
layer_property_header = ft.TextButton(
|
||||
text = "Layer Properties",
|
||||
icon = ft.icons.ARROW_RIGHT,
|
||||
on_click = open_close_layer_properties,
|
||||
disabled = True,
|
||||
)
|
||||
|
||||
layer_property_header.open = False
|
||||
|
||||
def update_layer_name(e):
|
||||
e.page.active_layer.label.value = e.control.value
|
||||
e.page.asset_manager.update()
|
||||
|
||||
layer_properties = ft.Column(
|
||||
visible = False,
|
||||
controls = [
|
||||
ft.Row(
|
||||
controls = [
|
||||
ft.TextField(
|
||||
label = 'Layer Name',
|
||||
value = '',
|
||||
text_align = 'center',
|
||||
content_padding = 0,
|
||||
expand = 1,
|
||||
on_submit = update_layer_name,
|
||||
),
|
||||
],
|
||||
),
|
||||
ft.Row(
|
||||
controls = [
|
||||
ft.TextField(
|
||||
label = 'Width',
|
||||
value = 0,
|
||||
text_align = 'center',
|
||||
content_padding = 0,
|
||||
expand = 1,
|
||||
),
|
||||
ft.TextField(
|
||||
label = 'Height',
|
||||
value = 0,
|
||||
text_align = 'center',
|
||||
content_padding = 0,
|
||||
expand = 1,
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
)
|
||||
|
||||
layer_property_divider = ft.Container(
|
||||
content = ft.Divider(),
|
||||
margin = 0,
|
||||
padding = 0,
|
||||
)
|
||||
|
||||
|
||||
property_panel = PropertyPanel(
|
||||
content = ft.Column(
|
||||
controls = [
|
||||
preview_pane,
|
||||
preview_dragbar,
|
||||
canvas_property_header,
|
||||
canvas_properties,
|
||||
canvas_property_divider,
|
||||
layer_property_header,
|
||||
layer_properties,
|
||||
layer_property_divider,
|
||||
],
|
||||
),
|
||||
)
|
||||
|
||||
property_panel.preview = preview_pane
|
||||
property_panel.preview_dragbar = preview_dragbar
|
||||
property_panel.canvas_property_header = canvas_property_header
|
||||
property_panel.canvas_properties_divider = canvas_property_divider
|
||||
property_panel.canvas_properties = canvas_properties
|
||||
property_panel.layer_property_header = layer_property_header
|
||||
property_panel.layer_properties = layer_properties
|
||||
property_panel.layer_properties_divider = layer_property_divider
|
||||
|
||||
output_panel = PropertyPanel(
|
||||
content = ft.Column(
|
||||
controls = [
|
||||
ft.Text("Under Construction."),
|
||||
],
|
||||
),
|
||||
)
|
||||
|
||||
def resize_property_manager(e):
|
||||
property_manager.resize_property_manager(e)
|
||||
|
||||
def realign_canvas(e):
|
||||
e.page.align_canvas()
|
||||
|
||||
property_manager_dragbar = ft.GestureDetector(
|
||||
mouse_cursor = ft.MouseCursor.RESIZE_COLUMN,
|
||||
drag_interval = 50,
|
||||
on_pan_update = resize_property_manager,
|
||||
on_pan_end = realign_canvas,
|
||||
content = ft.VerticalDivider()
|
||||
)
|
||||
|
||||
property_manager = PropertyManager(
|
||||
content = ft.Row(
|
||||
controls = [
|
||||
property_manager_dragbar,
|
||||
ft.Column(
|
||||
controls = [
|
||||
ft.Tabs(
|
||||
selected_index = 0,
|
||||
animation_duration = 300,
|
||||
tabs = [
|
||||
ft.Tab(
|
||||
content = property_panel,
|
||||
tab_content = ft.Text(
|
||||
value = "Properties",
|
||||
),
|
||||
),
|
||||
ft.Tab(
|
||||
content = output_panel,
|
||||
tab_content = ft.Text(
|
||||
value = "Output",
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
alignment = 'start',
|
||||
expand = True
|
||||
),
|
||||
ft.VerticalDivider(
|
||||
width = 4,
|
||||
opacity = 0,
|
||||
),
|
||||
],
|
||||
expand = True,
|
||||
),
|
||||
clip_behavior = 'antiAlias',
|
||||
)
|
||||
|
||||
property_manager.tabs = property_manager.content.controls[1].controls[0].tabs
|
||||
property_manager.dragbar = property_manager_dragbar
|
||||
property_manager.property_panel = property_panel
|
||||
property_manager.output_panel = output_panel
|
||||
|
203
webui/flet/scripts/flet_settings_window.py
Normal file
203
webui/flet/scripts/flet_settings_window.py
Normal file
@ -0,0 +1,203 @@
|
||||
# flet_settings_window.py
|
||||
|
||||
# Flet imports
|
||||
import flet as ft
|
||||
|
||||
# utils imports
|
||||
from scripts import flet_utils
|
||||
|
||||
|
||||
class SettingsWindow(ft.AlertDialog):
|
||||
def setup(self,settings):
|
||||
self.get_settings_window_tabs(settings)
|
||||
|
||||
def get_settings_window_tab_page_setting_slider(self,settings,section,setting,display_width):
|
||||
setting_slider = []
|
||||
setting_value = None
|
||||
if settings[setting]['value_type'] == 'int':
|
||||
setting_value = int(settings[setting]['value'])
|
||||
elif settings[setting]['value_type'] == 'float':
|
||||
setting_value = float(settings[setting]['value'])
|
||||
else:
|
||||
setting_value = settings[setting]['value']
|
||||
label = ft.Text(
|
||||
value = setting,
|
||||
text_align = 'center',
|
||||
)
|
||||
row = SettingsDisplay(
|
||||
width = display_width,
|
||||
data = [self, section, setting],
|
||||
controls = [],
|
||||
)
|
||||
slider = ft.Slider(
|
||||
value = setting_value,
|
||||
label = "{value}",
|
||||
min = settings[setting]['min'],
|
||||
max = settings[setting]['max'],
|
||||
divisions = int((settings[setting]['max'] - settings[setting]['min']) / settings[setting]['step']),
|
||||
on_change = row.settings_window_tab_slider_changed,
|
||||
data = row,
|
||||
expand = 4,
|
||||
)
|
||||
value = ft.TextField(
|
||||
value = setting_value,
|
||||
on_submit = row.settings_window_tab_slider_changed,
|
||||
data = row,
|
||||
content_padding = 10,
|
||||
expand = 1,
|
||||
)
|
||||
row.controls.extend([slider,value])
|
||||
setting_slider.extend([label,row])
|
||||
return setting_slider
|
||||
|
||||
def get_settings_window_tab_settings(self, settings, section):
|
||||
settings = settings[section]
|
||||
section_settings = [ft.Divider(height=10, color='gray')]
|
||||
display_width = (self.content.width * 0.5) - 5
|
||||
for setting in settings:
|
||||
if 'value' not in settings[setting]:
|
||||
continue
|
||||
new_row = SettingsDisplay()
|
||||
new_row
|
||||
display = None
|
||||
display_type = settings[setting]['display']
|
||||
if display_type == 'dropdown':
|
||||
option_list = []
|
||||
for i in range(len(settings[setting]['option_list'])):
|
||||
item = ft.dropdown.Option(
|
||||
text = settings[setting]['option_list'][i]
|
||||
)
|
||||
option_list.append(item)
|
||||
display = ft.Dropdown(
|
||||
label = setting,
|
||||
value = settings[setting]['value'],
|
||||
options = option_list,
|
||||
on_change = new_row.settings_window_tab_setting_changed,
|
||||
data = section,
|
||||
content_padding = 10,
|
||||
width = display_width,
|
||||
)
|
||||
elif display_type == 'textinput':
|
||||
display = ft.TextField(
|
||||
label = setting,
|
||||
value = settings[setting]['value'],
|
||||
on_submit = new_row.settings_window_tab_setting_changed,
|
||||
data = section,
|
||||
content_padding = 10,
|
||||
width = display_width,
|
||||
)
|
||||
elif display_type == 'bool':
|
||||
display = ft.Switch(
|
||||
label = setting,
|
||||
value = settings[setting]['value'],
|
||||
on_change = new_row.settings_window_tab_setting_changed,
|
||||
data = section,
|
||||
width = display_width,
|
||||
)
|
||||
elif display_type == 'slider':
|
||||
display = ft.Column(
|
||||
controls = self.get_settings_window_tab_page_setting_slider(settings,section,setting,display_width),
|
||||
)
|
||||
else:
|
||||
continue
|
||||
new_row.data = [self, section, setting]
|
||||
new_row.controls.append(display)
|
||||
section_settings.append(new_row)
|
||||
return section_settings
|
||||
|
||||
def get_settings_window_tab_page(self, settings, section):
|
||||
settings_window_tab_page = ft.Column(
|
||||
alignment = 'start',
|
||||
scroll = 'auto',
|
||||
controls = self.get_settings_window_tab_settings(settings, section),
|
||||
)
|
||||
return settings_window_tab_page
|
||||
|
||||
def get_settings_window_tabs(self, settings):
|
||||
tabs = []
|
||||
for section in settings:
|
||||
if section.endswith('_page'):
|
||||
tab = ft.Tab(
|
||||
text = section.split('_')[0],
|
||||
content = self.get_settings_window_tab_page(settings, section),
|
||||
)
|
||||
tabs.append(tab)
|
||||
self.content.content.tabs = tabs
|
||||
|
||||
def update_settings_window_tab(self, section):
|
||||
settings = self.page.session.get('settings')
|
||||
for i, tab in enumerate(self.content.content.tabs):
|
||||
if section.startswith(tab.text):
|
||||
self.content.content.tabs[i].content = self.get_settings_window_tab_page(settings, section)
|
||||
return
|
||||
|
||||
def update_settings_window(self):
|
||||
self.get_settings_window_tabs(self.page.session.get('settings'))
|
||||
self.page.update()
|
||||
|
||||
|
||||
class SettingsDisplay(ft.Row):
|
||||
def settings_window_tab_setting_changed(self, e):
|
||||
settings = self.page.session.get('settings')
|
||||
settings[e.control.data][e.control.label]['value'] = e.control.value
|
||||
update_settings_window_tab(e.control.data)
|
||||
self.page.update()
|
||||
|
||||
def settings_window_tab_slider_changed(self, e):
|
||||
settings = self.page.session.get('settings')
|
||||
parent = e.control.data
|
||||
setting = settings[parent.data[1]][parent.data[2]]
|
||||
setting_value = None
|
||||
if setting['value_type'] == 'int':
|
||||
setting_value = int(e.control.value)
|
||||
elif setting['value_type'] == 'float':
|
||||
setting_value = float(e.control.value)
|
||||
else:
|
||||
setting_value = e.control.value
|
||||
setting['value'] = setting_value
|
||||
parent.controls[0].value = setting_value
|
||||
parent.controls[1].value = str(setting_value)
|
||||
parent.data[0].update_settings_window_tab(parent.data[1])
|
||||
self.page.update()
|
||||
|
||||
def apply_settings(e):
|
||||
settings_window.update_settings_window()
|
||||
|
||||
def save_settings(e):
|
||||
save_settings_to_config()
|
||||
settings_window.update_settings_window()
|
||||
|
||||
def reset_settings(e):
|
||||
reset_settings_from_config()
|
||||
settings_window.update_settings_window()
|
||||
|
||||
# SettingsWindow == ft.AlertDialog
|
||||
settings_window = SettingsWindow(
|
||||
title = ft.Text("Settings"),
|
||||
content = ft.Container(
|
||||
content = ft.Tabs(
|
||||
selected_index = 0,
|
||||
animation_duration = 300,
|
||||
tabs = None,
|
||||
),
|
||||
),
|
||||
actions = [
|
||||
ft.ElevatedButton(
|
||||
text = "Apply",
|
||||
icon = ft.icons.CHECK_CIRCLE,
|
||||
on_click = apply_settings,
|
||||
),
|
||||
ft.ElevatedButton(
|
||||
text = "Save",
|
||||
icon = ft.icons.SAVE,
|
||||
on_click = save_settings,
|
||||
),
|
||||
ft.ElevatedButton(
|
||||
text = "Restore Defaults",
|
||||
icon = ft.icons.RESTORE_FROM_TRASH_ROUNDED,
|
||||
on_click = reset_settings,
|
||||
),
|
||||
],
|
||||
actions_alignment = "end",
|
||||
)
|
||||
|
134
webui/flet/scripts/flet_titlebar.py
Normal file
134
webui/flet/scripts/flet_titlebar.py
Normal file
@ -0,0 +1,134 @@
|
||||
# flet_appbar.py
|
||||
|
||||
# Flet imports
|
||||
import flet as ft
|
||||
|
||||
# utils imports
|
||||
from scripts import flet_utils
|
||||
|
||||
|
||||
class TitleBar(ft.Container):
|
||||
def setup(self):
|
||||
self.width = self.page.width
|
||||
self.height = self.page.titlebar_height
|
||||
|
||||
self.title.size = self.page.titlebar_height * 0.5
|
||||
self.title.color = self.page.tertiary_color
|
||||
|
||||
self.prompt.text_size = max(12, self.page.titlebar_height * 0.25)
|
||||
self.prompt.focused_border_color = self.page.tertiary_color
|
||||
|
||||
self.layout_menu.controls[0].text_size = self.page.text_size
|
||||
|
||||
self.theme_switcher.size = self.page.titlebar_height
|
||||
self.theme_switcher.icon_size = self.page.titlebar_height * 0.5
|
||||
self.theme_switcher.tooltip = f"Click to change between the light and dark themes. Current {'(Light theme)' if self.page.theme_mode == 'light' else '(Dark theme)'}"
|
||||
self.theme_switcher.on_click = self.page.change_theme_mode
|
||||
|
||||
self.settings_button.size = self.page.titlebar_height
|
||||
self.settings_button.icon_size = self.page.titlebar_height * 0.5
|
||||
self.settings_button.on_click = self.page.open_settings
|
||||
|
||||
def on_page_change(self):
|
||||
self.width = self.page.width
|
||||
self.height = self.page.titlebar_height
|
||||
|
||||
self.title.size = self.page.titlebar_height * 0.5
|
||||
self.title.color = self.page.tertiary_color
|
||||
|
||||
self.prompt.text_size = max(12, self.page.titlebar_height * 0.25)
|
||||
self.prompt.focused_border_color = self.page.tertiary_color
|
||||
|
||||
self.layout_menu.controls[0].text_size = self.page.text_size
|
||||
|
||||
self.theme_switcher.size = self.page.titlebar_height
|
||||
self.theme_switcher.icon_size = self.page.titlebar_height * 0.5
|
||||
self.theme_switcher.tooltip = f"Click to change between the light and dark themes. Current {'(Light theme)' if self.page.theme_mode == 'light' else '(Dark theme)'}"
|
||||
|
||||
self.settings_button.size = self.page.titlebar_height
|
||||
self.settings_button.icon_size = self.page.titlebar_height * 0.5
|
||||
|
||||
|
||||
title = ft.Text(
|
||||
value = " Sygil ",
|
||||
text_align = 'center',
|
||||
)
|
||||
|
||||
prompt = ft.TextField(
|
||||
value = "",
|
||||
min_lines = 1,
|
||||
max_lines = 1,
|
||||
content_padding = ft.padding.only(left = 12, top = 0, right = 0, bottom = 0),
|
||||
shift_enter = True,
|
||||
autofocus = True,
|
||||
expand = True,
|
||||
tooltip = "Prompt to use for generation.",
|
||||
hint_text = "A corgi wearing a top hat as an oil painting.",
|
||||
)
|
||||
|
||||
generate_button = ft.ElevatedButton(
|
||||
text = "Generate",
|
||||
on_click = None,
|
||||
)
|
||||
|
||||
def set_layout(e):
|
||||
e.page.set_layout(e)
|
||||
|
||||
layout_menu = ft.Row(
|
||||
alignment = 'start',
|
||||
controls = [
|
||||
ft.Dropdown(
|
||||
options = [
|
||||
ft.dropdown.Option(text="Default"),
|
||||
ft.dropdown.Option(text="Textual Inversion"),
|
||||
ft.dropdown.Option(text="Node Editor"),
|
||||
],
|
||||
value = 'Default',
|
||||
text_size = 20,
|
||||
# alignment = ft.alignment.center,
|
||||
content_padding = ft.padding.only(left = 12, top = 0, right = 0, bottom = 0),
|
||||
tooltip = "Switch between different workspaces",
|
||||
on_change = set_layout,
|
||||
)
|
||||
],
|
||||
)
|
||||
|
||||
layout_menu.text_size = layout_menu.controls[0].text_size
|
||||
|
||||
theme_switcher = ft.IconButton(
|
||||
ft.icons.WB_SUNNY_OUTLINED,
|
||||
)
|
||||
|
||||
settings_button = ft.IconButton(
|
||||
icon = ft.icons.SETTINGS,
|
||||
)
|
||||
|
||||
option_list = ft.Row(
|
||||
controls = [
|
||||
ft.Container(content = layout_menu),
|
||||
ft.Container(content = theme_switcher),
|
||||
ft.Container(content = settings_button),
|
||||
],
|
||||
alignment = 'end'
|
||||
)
|
||||
|
||||
|
||||
# TitleBar == ft.Container
|
||||
titlebar = TitleBar(
|
||||
content = ft.Row(
|
||||
controls = [
|
||||
title,
|
||||
prompt,
|
||||
#generate_button,
|
||||
option_list,
|
||||
],
|
||||
),
|
||||
)
|
||||
|
||||
titlebar.title = title
|
||||
titlebar.prompt = prompt
|
||||
titlebar.generate_button = generate_button
|
||||
titlebar.layout_menu = layout_menu
|
||||
titlebar.theme_switcher = theme_switcher
|
||||
titlebar.settings_button = settings_button
|
||||
|
245
webui/flet/scripts/flet_tool_manager.py
Normal file
245
webui/flet/scripts/flet_tool_manager.py
Normal file
@ -0,0 +1,245 @@
|
||||
# flet_tool_manager.py
|
||||
|
||||
# Flet imports
|
||||
import flet as ft
|
||||
|
||||
# utils imports
|
||||
from scripts import flet_utils
|
||||
|
||||
|
||||
def open_gallery(e):
|
||||
e.page.open_gallery(e)
|
||||
|
||||
def blank_layer(e):
|
||||
e.page.add_blank_layer()
|
||||
|
||||
def load_images(e):
|
||||
e.page.load_images()
|
||||
|
||||
def tool_select(e):
|
||||
e.page.set_current_tool(e)
|
||||
|
||||
|
||||
class Action():
|
||||
def __init__(self, label, icon, tooltip, on_click):
|
||||
self.label = label
|
||||
self.icon = icon
|
||||
self.tooltip = tooltip
|
||||
self.on_click = on_click
|
||||
self.disabled = False
|
||||
|
||||
action_list = [
|
||||
Action('gallery', ft.icons.DASHBOARD_OUTLINED, 'Gallery', open_gallery),
|
||||
Action('blank layer', ft.icons.ADD_OUTLINED, 'Add blank layer', blank_layer),
|
||||
Action('load image', ft.icons.IMAGE_OUTLINED, 'Load image as layer', load_images),
|
||||
]
|
||||
|
||||
|
||||
class Tool():
|
||||
def __init__(self, label, icon, tooltip):
|
||||
self.label = label
|
||||
self.icon = icon
|
||||
self.tooltip = tooltip
|
||||
self.on_click = tool_select
|
||||
self.disabled = True
|
||||
|
||||
tool_list = [
|
||||
Tool('move', ft.icons.OPEN_WITH_OUTLINED, 'Move layer(s)'),
|
||||
Tool('select', ft.icons.HIGHLIGHT_ALT_OUTLINED, 'Select tool'),
|
||||
Tool('brush', ft.icons.BRUSH_OUTLINED, 'Brush tool'),
|
||||
Tool('fill', ft.icons.FORMAT_COLOR_FILL_OUTLINED, 'Fill tool'),
|
||||
]
|
||||
|
||||
|
||||
class ToolManager(ft.Container):
|
||||
def setup(self):
|
||||
self.toolbox.get_tools()
|
||||
self.width = self.page.tool_manager_width
|
||||
self.bgcolor = self.page.primary_color
|
||||
self.padding = self.page.container_padding
|
||||
self.margin = self.page.container_margin
|
||||
|
||||
self.toolbox.bgcolor = self.page.secondary_color
|
||||
self.toolbox.padding = self.page.container_padding
|
||||
self.toolbox.margin = self.page.container_margin
|
||||
|
||||
self.tool_divider.height = self.page.divider_height
|
||||
self.tool_divider.color = self.page.tertiary_color
|
||||
|
||||
self.tool_properties.bgcolor = self.page.secondary_color
|
||||
self.tool_properties.padding = self.page.container_padding
|
||||
self.tool_properties.margin = self.page.container_margin
|
||||
|
||||
self.dragbar.width = self.page.vertical_divider_width
|
||||
self.dragbar.color = self.page.tertiary_color
|
||||
|
||||
def on_page_change(self):
|
||||
self.width = self.page.tool_manager_width
|
||||
self.bgcolor = self.page.primary_color
|
||||
self.padding = self.page.container_padding
|
||||
self.margin = self.page.container_margin
|
||||
|
||||
self.toolbox.bgcolor = self.page.secondary_color
|
||||
self.toolbox.padding = self.page.container_padding
|
||||
self.toolbox.margin = self.page.container_margin
|
||||
|
||||
self.tool_divider.height = self.page.divider_height
|
||||
self.tool_divider.color = self.page.tertiary_color
|
||||
|
||||
self.tool_properties.bgcolor = self.page.secondary_color
|
||||
self.tool_properties.padding = self.page.container_padding
|
||||
self.tool_properties.margin = self.page.container_margin
|
||||
|
||||
self.dragbar.width = self.page.vertical_divider_width
|
||||
self.dragbar.color = self.page.tertiary_color
|
||||
|
||||
|
||||
def resize_tool_manager(self, e: ft.DragUpdateEvent):
|
||||
self.page.tool_manager_width = max(50, self.page.tool_manager_width + e.delta_x)
|
||||
tool_manager.width = self.page.tool_manager_width
|
||||
self.page.update()
|
||||
|
||||
def resize_toolbox(self, e: ft.DragUpdateEvent):
|
||||
min_height = (self.page.tool_manager_button_size * 2)
|
||||
self.page.toolbox_height = max(min_height, self.page.toolbox_height + e.delta_y)
|
||||
toolbox.height = self.page.toolbox_height
|
||||
self.update()
|
||||
|
||||
def enable_tools(self):
|
||||
for tool in self.toolbox.content.controls:
|
||||
try:
|
||||
if tool.on_click == tool_select:
|
||||
tool.disabled = False
|
||||
except AttributeError:
|
||||
continue # is divider
|
||||
self.update()
|
||||
|
||||
def disable_tools(self):
|
||||
for tool in self.toolbox.content.controls:
|
||||
try:
|
||||
if tool.on_click == tool_select:
|
||||
tool.disabled = True
|
||||
except AttributeError:
|
||||
continue # is divider
|
||||
self.update()
|
||||
|
||||
def clear_tools(self):
|
||||
self.toolbox.clear_tools()
|
||||
|
||||
|
||||
class ToolBox(ft.Container):
|
||||
def get_tools(self):
|
||||
for action in action_list:
|
||||
self.content.controls.append(self.make_button(action))
|
||||
divider = ft.Container(
|
||||
content = ft.Divider(
|
||||
height = self.page.divider_height,
|
||||
color = self.page.tertiary_color,
|
||||
),
|
||||
margin = 0,
|
||||
padding = ft.padding.only(left = 10, top = 0, right = 0, bottom = 0),
|
||||
)
|
||||
self.content.controls.append(divider)
|
||||
for tool in tool_list:
|
||||
self.content.controls.append(self.make_button(tool))
|
||||
tool_manager.update()
|
||||
|
||||
def make_button(self,button_info):
|
||||
button = ft.IconButton(
|
||||
width = self.page.icon_size * 2,
|
||||
icon_size = self.page.icon_size,
|
||||
content = ft.Icon(button_info.icon),
|
||||
selected = False,
|
||||
selected_icon_color = self.page.tertiary_color,
|
||||
tooltip = button_info.tooltip,
|
||||
data = {'label':button_info.label},
|
||||
on_click = button_info.on_click,
|
||||
disabled = button_info.disabled,
|
||||
)
|
||||
return button
|
||||
|
||||
def clear_tools(self):
|
||||
for control in self.content.controls:
|
||||
control.selected = False
|
||||
|
||||
|
||||
class ToolPropertyPanel(ft.Container):
|
||||
pass
|
||||
|
||||
|
||||
# ToolBox == ft.Container
|
||||
toolbox = ToolBox(
|
||||
clip_behavior = 'antiAlias',
|
||||
content = ft.Row(
|
||||
alignment = 'start',
|
||||
wrap = True,
|
||||
spacing = 0,
|
||||
run_spacing = 0,
|
||||
controls = [],
|
||||
),
|
||||
margin = 0,
|
||||
padding = ft.padding.only(left = 15, top = 0, right = 0, bottom = 0),
|
||||
)
|
||||
|
||||
def resize_toolbox(e):
|
||||
tool_manager.resize_toolbox(e)
|
||||
|
||||
tool_divider = ft.GestureDetector(
|
||||
mouse_cursor = ft.MouseCursor.RESIZE_ROW,
|
||||
drag_interval = 50,
|
||||
on_pan_update = resize_toolbox,
|
||||
content = ft.Container(
|
||||
content = ft.Divider(),
|
||||
margin = 0,
|
||||
padding = ft.padding.only(left = 10, top = 0, right = 0, bottom = 0),
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
# ToolPropertyPanel == ft.Container
|
||||
tool_properties = ToolPropertyPanel(
|
||||
content = ft.Column(
|
||||
controls = [],
|
||||
)
|
||||
)
|
||||
|
||||
def resize_tool_manager(e):
|
||||
tool_manager.resize_tool_manager(e)
|
||||
|
||||
def realign_canvas(e):
|
||||
e.page.align_canvas()
|
||||
|
||||
tool_manager_dragbar = ft.GestureDetector(
|
||||
mouse_cursor = ft.MouseCursor.RESIZE_COLUMN,
|
||||
drag_interval = 50,
|
||||
on_pan_update = resize_tool_manager,
|
||||
on_pan_end = realign_canvas,
|
||||
content = ft.VerticalDivider(),
|
||||
)
|
||||
|
||||
|
||||
# ToolManager = ft.Container
|
||||
tool_manager = ToolManager(
|
||||
content = ft.Row(
|
||||
controls = [
|
||||
ft.Column(
|
||||
controls = [
|
||||
toolbox,
|
||||
tool_divider,
|
||||
tool_properties,
|
||||
],
|
||||
alignment = 'start',
|
||||
expand = True,
|
||||
),
|
||||
tool_manager_dragbar,
|
||||
],
|
||||
expand = True,
|
||||
),
|
||||
clip_behavior = 'antiAlias',
|
||||
)
|
||||
|
||||
tool_manager.toolbox = toolbox
|
||||
tool_manager.tool_divider = tool_divider.content.content
|
||||
tool_manager.tool_properties = tool_properties
|
||||
tool_manager.dragbar = tool_manager_dragbar.content
|
||||
|
175
webui/flet/scripts/flet_utils.py
Normal file
175
webui/flet/scripts/flet_utils.py
Normal file
@ -0,0 +1,175 @@
|
||||
# flet_utils.py
|
||||
|
||||
# imports
|
||||
import os, yaml, base64
|
||||
from PIL import Image, ImageDraw
|
||||
from io import BytesIO
|
||||
from datetime import datetime
|
||||
from pprint import pprint
|
||||
|
||||
|
||||
# logging
|
||||
def log_message(message):
|
||||
log_file = None
|
||||
# get time and format message
|
||||
prefix = datetime.now()
|
||||
msg_prefix = prefix.strftime("%Y/%m/%d %H:%M:%S")
|
||||
message = msg_prefix + " " + message
|
||||
# check to see if we're appending to current logfile or making a new one'
|
||||
try:
|
||||
log_file = log_message.log
|
||||
except AttributeError:
|
||||
log_prefix = prefix.strftime("%Y%m%d_%H%M%S")
|
||||
os.makedirs('log/webui/flet', exist_ok=True)
|
||||
log_message.log = os.path.join('log/webui/flet',log_prefix+'webui_flet.log')
|
||||
log_file = log_message.log
|
||||
# write message to logfile
|
||||
with open(log_file,'a+') as log:
|
||||
log.write(message)
|
||||
|
||||
# settings
|
||||
path_to_default_config = 'configs/webui/webui_flet.yaml'
|
||||
path_to_user_config = 'configs/webui/userconfig_flet.yaml'
|
||||
|
||||
def get_default_settings_from_config():
|
||||
with open(path_to_default_config) as f:
|
||||
default_settings = yaml.safe_load(f)
|
||||
return default_settings
|
||||
|
||||
def get_user_settings_from_config():
|
||||
# get default settings
|
||||
settings = get_default_settings_from_config()
|
||||
# check to see if userconfig exists
|
||||
if os.path.exists(path_to_user_config):
|
||||
# compare to see which is newer
|
||||
default_time = os.path.getmtime(path_to_default_config)
|
||||
user_time = os.path.getmtime(path_to_user_config)
|
||||
# if default is newer, save over userconfig and exit early
|
||||
if (default_time > user_time):
|
||||
save_user_settings_to_config(settings)
|
||||
return settings
|
||||
# else, load userconfig
|
||||
with open(path_to_user_config) as f:
|
||||
user_settings = yaml.safe_load(f)
|
||||
settings.update(user_settings)
|
||||
# regardless, return settings as dict
|
||||
return settings
|
||||
|
||||
def save_user_settings_to_config(settings):
|
||||
with open(path_to_user_config, 'w+') as f:
|
||||
yaml.dump(settings, f, default_flow_style=False)
|
||||
|
||||
|
||||
# image handling
|
||||
path_to_assets = "webui/flet/assets"
|
||||
path_to_uploads = "webui/flet/assets/uploads"
|
||||
path_to_outputs = "webui/flet/assets/outputs"
|
||||
|
||||
# creates blank image (to do: take size as arg)
|
||||
def create_blank_image(size):
|
||||
try:
|
||||
create_blank_image.count +=1
|
||||
except AttributeError:
|
||||
create_blank_image.count = 1
|
||||
name = 'blank_layer_' + str(create_blank_image.count).zfill(2)
|
||||
img = Image.new('RGBA',size,(0,0,0,0))
|
||||
img.filename = name
|
||||
img.path = None
|
||||
return img
|
||||
|
||||
# takes name of image
|
||||
# returns dict
|
||||
# name of image : image handle
|
||||
def get_image_from_uploads(name):
|
||||
path_to_image = os.path.join(path_to_uploads, name)
|
||||
if os.path.exists(path_to_image):
|
||||
image = Image.open(path_to_image)
|
||||
image = image.convert("RGBA")
|
||||
image.filename = name
|
||||
image.path = path_to_image
|
||||
return image
|
||||
else:
|
||||
log_message(f'image not found: "{name}"')
|
||||
return None
|
||||
|
||||
def get_canvas_background(path):
|
||||
image = Image.open(path)
|
||||
image = image.convert("RGBA")
|
||||
return image
|
||||
|
||||
# make canvas frame
|
||||
def get_canvas_frame(canvas_size):
|
||||
image = Image.new('RGBA',(4096,4096),(0,0,0,127))
|
||||
canvas_width = canvas_size[0]
|
||||
canvas_height = canvas_size[1]
|
||||
x0 = int((image.width - canvas_width) * 0.5)
|
||||
y0 = int((image.height - canvas_height) * 0.5)
|
||||
x1 = x0 + canvas_width
|
||||
y1 = y0 + canvas_height
|
||||
box = (x0, y0, x1, y1)
|
||||
image.paste((0,0,0,0), box)
|
||||
return convert_image_to_base64(image)
|
||||
|
||||
# takes list of Image(s) as arg
|
||||
# returns single composite of all images
|
||||
def get_preview_from_stack(size, stack):
|
||||
preview = Image.new('RGBA',size,(0,0,0,0))
|
||||
canvas_width = size[0]
|
||||
canvas_height = size[1]
|
||||
for layer in stack:
|
||||
image = layer.image
|
||||
# need to crop images for composite
|
||||
x0 = ((image.width - canvas_width) * 0.5) - layer.offset_x
|
||||
y0 = ((image.height - canvas_height) * 0.5) - layer.offset_y
|
||||
x1 = x0 + canvas_width
|
||||
y1 = y0 + canvas_height
|
||||
box = (x0, y0, x1, y1)
|
||||
cropped_image = image.crop(box)
|
||||
preview = Image.alpha_composite(preview,cropped_image)
|
||||
return preview
|
||||
|
||||
# converts Image to base64 string
|
||||
def convert_image_to_base64(image):
|
||||
image_buffer = BytesIO()
|
||||
image.save(image_buffer, format='PNG')
|
||||
image_buffer.seek(0)
|
||||
image_bytes = image_buffer.getvalue()
|
||||
return base64.b64encode(image_bytes).decode()
|
||||
|
||||
# takes name of gallery as arg ('assets','output','uploads')
|
||||
# returns list of dicts
|
||||
# name of image:
|
||||
# 'img_path' : path_to_image
|
||||
# 'info_path' : path_to_yaml
|
||||
def get_gallery_images(gallery_name):
|
||||
path_to_gallery = None
|
||||
images = []
|
||||
files = []
|
||||
if gallery_name == 'uploads':
|
||||
path_to_gallery = path_to_uploads
|
||||
elif gallery_name == 'outputs':
|
||||
path_to_gallery = path_to_outputs
|
||||
else:
|
||||
log_message(f'gallery not found: "{gallery_name}"')
|
||||
return images
|
||||
if os.path.exists(path_to_gallery):
|
||||
files = os.listdir(path_to_gallery)
|
||||
else:
|
||||
return None
|
||||
for f in files:
|
||||
if f.endswith(('.jpg','.jpeg','.png')):
|
||||
image = Image.open(os.path.join(path_to_gallery,f))
|
||||
image = image.convert("RGBA")
|
||||
image.filename = f
|
||||
image.path = os.path.join(gallery_name,f)
|
||||
images.append(image)
|
||||
return images
|
||||
|
||||
|
||||
# textual inversion
|
||||
textual_inversion_grid_row_list = [
|
||||
'model', 'medium', 'artist', 'trending', 'movement', 'flavors', 'techniques', 'tags',
|
||||
]
|
||||
|
||||
def run_textual_inversion(args):
|
||||
pass
|
447
webui/flet/webui_flet.py
Normal file
447
webui/flet/webui_flet.py
Normal file
@ -0,0 +1,447 @@
|
||||
# Flet imports
|
||||
import flet as ft
|
||||
|
||||
# other imports
|
||||
import os
|
||||
from math import pi
|
||||
from typing import Optional
|
||||
from loguru import logger
|
||||
import logging
|
||||
#logging.basicConfig(level=logging.DEBUG)
|
||||
|
||||
# utils imports
|
||||
from scripts import flet_utils
|
||||
from scripts.flet_settings_window import settings_window
|
||||
from scripts.flet_gallery_window import gallery_window
|
||||
from scripts.flet_file_manager import file_picker, uploads, imports
|
||||
from scripts.flet_titlebar import titlebar
|
||||
from scripts.flet_tool_manager import tool_manager
|
||||
from scripts.flet_asset_manager import asset_manager, layer_action_menu
|
||||
from scripts.flet_canvas import canvas
|
||||
from scripts.flet_messages import messages
|
||||
from scripts.flet_property_manager import property_manager
|
||||
|
||||
# for debugging
|
||||
from pprint import pprint
|
||||
|
||||
os.environ["FLET_WS_MAX_MESSAGE_SIZE"] = "8000000"
|
||||
|
||||
|
||||
# main ###############################################################
|
||||
@logger.catch(reraise=True)
|
||||
def main(page: ft.Page):
|
||||
|
||||
# init ###############################################################
|
||||
# messages
|
||||
page.messages = messages
|
||||
page.message = messages.message
|
||||
page.max_message_history = 50
|
||||
|
||||
|
||||
# ui
|
||||
page.current_layout = 'Default'
|
||||
page.titlebar_height = 50
|
||||
page.bottom_panel_height = page.height * 0.2
|
||||
page.toolbox_height = 250
|
||||
page.tool_manager_width = 50
|
||||
page.tool_manager_button_size = 40
|
||||
page.left_panel_width = 200
|
||||
page.right_panel_width = 250
|
||||
|
||||
page.background_color = None
|
||||
page.primary_color = None
|
||||
page.secondary_color = 'white_10'
|
||||
page.tertiary_color = 'blue'
|
||||
|
||||
page.text_color = None
|
||||
page.text_size = 14
|
||||
page.icon_size = 20
|
||||
|
||||
page.padding = 0
|
||||
page.margin = 0
|
||||
page.container_padding = 0
|
||||
page.container_margin = 0
|
||||
|
||||
page.tab_color = 'white_10'
|
||||
page.tab_padding = ft.padding.only(left = 2, top = 12, right = 2, bottom = 8)
|
||||
page.tab_margin = 0
|
||||
|
||||
page.divider_height = 10
|
||||
page.vertical_divider_width = 10
|
||||
|
||||
|
||||
# titlebar
|
||||
page.titlebar = titlebar
|
||||
|
||||
def change_theme_mode(e):
|
||||
page.theme_mode = "dark" if page.theme_mode == "light" else "light"
|
||||
|
||||
if "(Light theme)" in titlebar.theme_switcher.tooltip:
|
||||
titlebar.theme_switcher.tooltip = titlebar.theme_switcher.tooltip.replace("(Light theme)", '')
|
||||
|
||||
if "(Dark theme)" in titlebar.theme_switcher.tooltip:
|
||||
titlebar.theme_switcher.tooltip = titlebar.theme_switcher.tooltip.replace("(Dark theme)", '')
|
||||
|
||||
titlebar.theme_switcher.tooltip += "(Light theme)" if page.theme_mode == "light" else "(Dark theme)"
|
||||
page.update()
|
||||
|
||||
page.change_theme_mode = change_theme_mode
|
||||
|
||||
|
||||
# tools
|
||||
page.tool_manager = tool_manager
|
||||
page.current_tool = 'pan'
|
||||
|
||||
def enable_tools():
|
||||
page.tool_manager.enable_tools()
|
||||
|
||||
page.enable_tools = enable_tools
|
||||
|
||||
def disable_tools():
|
||||
page.tool_manager.disable_tools()
|
||||
|
||||
page.disable_tools = disable_tools
|
||||
|
||||
def set_current_tool(e):
|
||||
page.tool_manager.clear_tools()
|
||||
page.canvas.clear_tools()
|
||||
e.control.selected = True
|
||||
page.current_tool = e.control.data['label']
|
||||
page.canvas.set_current_tool(e.control.data['label'])
|
||||
page.update()
|
||||
|
||||
page.set_current_tool = set_current_tool
|
||||
|
||||
|
||||
# asset manager
|
||||
page.asset_manager = asset_manager
|
||||
page.active_layer = None
|
||||
page.visible_layers = []
|
||||
page.layer_height = 50
|
||||
|
||||
def set_active_layer(layer_slot):
|
||||
if page.active_layer == layer_slot:
|
||||
return
|
||||
page.active_layer = layer_slot
|
||||
page.enable_tools()
|
||||
page.property_manager.refresh_layer_properties()
|
||||
|
||||
page.set_active_layer = set_active_layer
|
||||
|
||||
def add_blank_layer():
|
||||
image = flet_utils.create_blank_image(page.canvas_size)
|
||||
layer_slot = page.asset_manager.add_image_as_layer(image)
|
||||
layer_slot.layer_image = page.canvas.add_layer_image(image)
|
||||
page.message("added blank layer to canvas")
|
||||
page.refresh_layers()
|
||||
|
||||
page.add_blank_layer = add_blank_layer
|
||||
|
||||
def add_images_as_layers(images):
|
||||
layer_slots = page.asset_manager.add_images_as_layers(images)
|
||||
for slot in layer_slots:
|
||||
slot.layer_image = page.canvas.add_layer_image(slot.image)
|
||||
page.message(f'added "{slot.image.filename}" as layer')
|
||||
page.refresh_layers()
|
||||
|
||||
page.add_images_as_layers = add_images_as_layers
|
||||
|
||||
def load_images():
|
||||
page.file_picker.pick_files(file_type = 'image', allow_multiple = True)
|
||||
|
||||
page.load_images = load_images
|
||||
|
||||
|
||||
# canvas
|
||||
page.canvas = canvas
|
||||
page.canvas_background = flet_utils.get_canvas_background('webui/flet/assets/images/default_grid_texture.png')
|
||||
page.canvas_size = [512,512]
|
||||
|
||||
def get_viewport_size():
|
||||
viewport_width = page.width - (page.tool_manager_width + (page.vertical_divider_width * 3) + page.left_panel_width + page.right_panel_width)
|
||||
viewport_height = page.height - (page.titlebar_height * 2) - page.bottom_panel_height
|
||||
return viewport_width, viewport_height
|
||||
|
||||
page.get_viewport_size = get_viewport_size
|
||||
|
||||
|
||||
def align_canvas():
|
||||
page.canvas.align_canvas()
|
||||
|
||||
page.align_canvas = align_canvas
|
||||
|
||||
|
||||
# property manager
|
||||
page.property_manager = property_manager
|
||||
|
||||
def refresh_canvas_preview():
|
||||
preview = page.canvas.get_image_stack_preview()
|
||||
page.property_manager.set_preview_image(preview)
|
||||
|
||||
page.refresh_canvas_preview = refresh_canvas_preview
|
||||
|
||||
def refresh_layers():
|
||||
if page.active_layer == None:
|
||||
page.disable_tools()
|
||||
else:
|
||||
page.enable_tools()
|
||||
page.asset_manager.refresh_layers()
|
||||
page.canvas.refresh_canvas()
|
||||
page.refresh_canvas_preview()
|
||||
page.property_manager.refresh_layer_properties()
|
||||
page.update()
|
||||
|
||||
page.refresh_layers = refresh_layers
|
||||
|
||||
|
||||
# layouts
|
||||
def set_layout(e):
|
||||
page.current_layout = e.control.value
|
||||
page.update()
|
||||
|
||||
page.set_layout = set_layout
|
||||
|
||||
|
||||
def on_page_change(e):
|
||||
page.titlebar.on_page_change()
|
||||
page.tool_manager.on_page_change()
|
||||
page.asset_manager.on_page_change()
|
||||
page.canvas.on_page_change()
|
||||
page.messages.on_page_change()
|
||||
page.property_manager.on_page_change()
|
||||
full_page.width = page.width
|
||||
full_page.height = page.height
|
||||
page.update()
|
||||
|
||||
page.on_resize = on_page_change
|
||||
|
||||
def on_window_change(e):
|
||||
if e.data == 'minimize' or e.data == 'close':
|
||||
return
|
||||
else:
|
||||
page.on_page_change(e)
|
||||
|
||||
page.on_window_event = on_window_change
|
||||
|
||||
# settings
|
||||
def load_settings():
|
||||
settings = flet_utils.get_user_settings_from_config()
|
||||
page.session.set('settings',settings)
|
||||
page.update()
|
||||
|
||||
def save_settings_to_config():
|
||||
settings = page.session.get('settings')
|
||||
flet_utils.save_user_settings_to_config(settings)
|
||||
|
||||
def reset_settings_from_config():
|
||||
settings = flet_utils.get_default_settings_from_config()
|
||||
page.session.remove('settings')
|
||||
page.session.set('settings',settings)
|
||||
save_settings_to_config()
|
||||
|
||||
if not page.session.contains_key('settings'):
|
||||
load_settings()
|
||||
settings = page.session.get('settings')
|
||||
try:
|
||||
ui_settings = settings['webui_page']
|
||||
page.theme_mode = ui_settings['default_theme']['value']
|
||||
MAX_MESSAGE_HISTORY = ui_settings['max_message_history']['value']
|
||||
except AttributeError:
|
||||
page.message("Config load error: missing setting.",1)
|
||||
|
||||
page.session.set('layout','default')
|
||||
|
||||
|
||||
# settings window ####################################################
|
||||
|
||||
def close_settings_window(e):
|
||||
settings_window.open = False
|
||||
page.update()
|
||||
|
||||
page.close_settings = close_settings_window
|
||||
|
||||
def open_settings_window(e):
|
||||
page.dialog = settings_window
|
||||
settings_window.open = True
|
||||
page.update()
|
||||
|
||||
page.open_settings = open_settings_window
|
||||
|
||||
page.settings_window = settings_window
|
||||
settings_window.content.width = page.width * 0.50
|
||||
settings_window.content.bgcolor = page.primary_color
|
||||
settings_window.content.padding = page.container_padding
|
||||
settings_window.content.margin = page.container_margin
|
||||
|
||||
|
||||
# gallery window #####################################################
|
||||
|
||||
def close_gallery_window(e):
|
||||
gallery_window.open = False
|
||||
page.update()
|
||||
|
||||
page.close_gallery = close_gallery_window
|
||||
|
||||
def open_gallery_window(e):
|
||||
page.dialog = gallery_window
|
||||
gallery_window.open = True
|
||||
page.update()
|
||||
|
||||
page.open_gallery = open_gallery_window
|
||||
|
||||
page.gallery_window = gallery_window
|
||||
page.refresh_gallery = gallery_window.refresh_gallery
|
||||
gallery_window.content.width = page.width * 0.5
|
||||
gallery_window.content.bgcolor = page.primary_color
|
||||
gallery_window.content.padding = page.container_padding
|
||||
gallery_window.content.margin = page.container_margin
|
||||
|
||||
gallery_window.outputs_gallery.height = page.height * 0.75
|
||||
gallery_window.outputs_gallery.bgcolor = page.primary_color
|
||||
gallery_window.outputs_gallery.padding = page.container_padding
|
||||
gallery_window.outputs_gallery.margin = page.container_margin
|
||||
|
||||
gallery_window.uploads_gallery.height = page.height * 0.75
|
||||
gallery_window.uploads_gallery.bgcolor = page.primary_color
|
||||
gallery_window.uploads_gallery.padding = page.container_padding
|
||||
gallery_window.uploads_gallery.margin = page.container_margin
|
||||
|
||||
|
||||
# file manager #######################################################
|
||||
|
||||
def close_upload_window(e):
|
||||
uploads.open = False
|
||||
page.update()
|
||||
|
||||
page.close_uploads = close_upload_window
|
||||
|
||||
def open_upload_window(e):
|
||||
page.dialog = uploads
|
||||
uploads.open = True
|
||||
page.update()
|
||||
|
||||
page.open_uploads = open_upload_window
|
||||
|
||||
def close_import_window(e):
|
||||
imports.open = False
|
||||
page.update()
|
||||
|
||||
page.close_imports = close_import_window
|
||||
|
||||
def open_import_window(e):
|
||||
page.dialog = imports
|
||||
imports.open = True
|
||||
page.update()
|
||||
|
||||
page.open_imports = open_import_window
|
||||
|
||||
page.uploads = uploads
|
||||
page.imports = imports
|
||||
page.file_picker = file_picker
|
||||
page.overlay.append(file_picker)
|
||||
|
||||
|
||||
# center panel #############################################################
|
||||
|
||||
text_editor = ft.Container(
|
||||
content = ft.Text('Under Construction.'),
|
||||
bgcolor = page.secondary_color,
|
||||
expand = True,
|
||||
)
|
||||
|
||||
viewport = ft.Container(
|
||||
bgcolor = page.primary_color,
|
||||
padding = page.container_padding,
|
||||
margin = page.container_margin,
|
||||
content = ft.Tabs(
|
||||
selected_index = 0,
|
||||
animation_duration = 300,
|
||||
tabs = [
|
||||
ft.Tab(
|
||||
content = canvas,
|
||||
tab_content = ft.Text(
|
||||
value = 'Canvas',
|
||||
size = page.text_size,
|
||||
),
|
||||
),
|
||||
ft.Tab(
|
||||
text = 'Text Editor',
|
||||
content = text_editor,
|
||||
tab_content = ft.Text(
|
||||
value = 'Text Editor',
|
||||
size = page.text_size,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
expand = True,
|
||||
)
|
||||
|
||||
center_panel = ft.Container(
|
||||
content = ft.Column(
|
||||
controls = [
|
||||
viewport,
|
||||
messages,
|
||||
],
|
||||
),
|
||||
bgcolor = page.primary_color,
|
||||
padding = page.container_padding,
|
||||
margin = page.container_margin,
|
||||
expand = True,
|
||||
)
|
||||
|
||||
|
||||
# layouts ############################################################
|
||||
|
||||
default_layout = ft.Row(
|
||||
controls = [
|
||||
tool_manager,
|
||||
asset_manager,
|
||||
center_panel,
|
||||
property_manager,
|
||||
],
|
||||
expand=True,
|
||||
)
|
||||
|
||||
current_layout = default_layout
|
||||
|
||||
|
||||
# workspace ##########################################################
|
||||
|
||||
workspace = ft.Column(
|
||||
controls = [
|
||||
titlebar,
|
||||
current_layout,
|
||||
],
|
||||
expand = True,
|
||||
)
|
||||
|
||||
page.workspace = workspace
|
||||
|
||||
full_page = ft.Stack(
|
||||
expand = True,
|
||||
controls = [
|
||||
workspace,
|
||||
layer_action_menu,
|
||||
],
|
||||
height = page.height,
|
||||
width = page.width,
|
||||
)
|
||||
|
||||
page.full_page = full_page
|
||||
|
||||
page.title = "Stable Diffusion Playground"
|
||||
page.add(full_page)
|
||||
|
||||
page.settings_window.setup(page.session.get('settings'))
|
||||
page.gallery_window.setup()
|
||||
page.titlebar.setup()
|
||||
page.tool_manager.setup()
|
||||
page.asset_manager.setup()
|
||||
page.canvas.setup()
|
||||
page.messages.setup()
|
||||
page.property_manager.setup()
|
||||
page.update()
|
||||
|
||||
|
||||
ft.app(target=main, route_url_strategy="path", port=8505, assets_dir="assets", upload_dir="assets/uploads")
|
Loading…
Reference in New Issue
Block a user