Skip to content

Commit 8fd70f3

Browse files
authored
Zentk integration (Zenity/yad support) (LostRuins#1475)
* Zentk integration (Zenity/yad support) * Escape incompatible dependencies in zentk * Properly clean env
1 parent e2fefc3 commit 8fd70f3

File tree

1 file changed

+118
-15
lines changed

1 file changed

+118
-15
lines changed

koboldcpp.py

Lines changed: 118 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
import urllib.parse as urlparse
3030
from concurrent.futures import ThreadPoolExecutor
3131
from datetime import datetime, timezone
32+
from typing import Tuple
3233

3334
# constants
3435
sampler_order_max = 7
@@ -615,9 +616,8 @@ def unpack_to_dir(destpath = ""):
615616
print("Attempt to unpack KoboldCpp into directory...")
616617

617618
if not cliunpack:
618-
from tkinter.filedialog import askdirectory
619619
from tkinter import messagebox
620-
destpath = askdirectory(title='Select an empty folder to unpack KoboldCpp')
620+
destpath = zentk_askdirectory(title='Select an empty folder to unpack KoboldCpp')
621621
if not destpath:
622622
return
623623

@@ -3455,19 +3455,123 @@ def stop(self):
34553455
threadArr[i].stop()
34563456
sys.exit(0)
34573457

3458+
# Based on https://github.com/mathgeniuszach/xdialog/blob/main/xdialog/zenity_dialogs.py - MIT license | - Expanded version by Henk717
3459+
3460+
def zenity_clean(txt: str):
3461+
return txt\
3462+
.replace("\\", "\\\\")\
3463+
.replace("$", "\\$")\
3464+
.replace("!", "\\!")\
3465+
.replace("*", "\\*")\
3466+
.replace("?", "\\?")\
3467+
.replace("&", "&")\
3468+
.replace("|", "|")\
3469+
.replace("<", "&lt;")\
3470+
.replace(">", "&gt;")\
3471+
.replace("(", "\\(")\
3472+
.replace(")", "\\)")\
3473+
.replace("[", "\\[")\
3474+
.replace("]", "\\]")\
3475+
.replace("{", "\\{")\
3476+
.replace("}", "\\}")\
3477+
3478+
def zenity(typ, filetypes=None, initialdir="", initialfile="", **kwargs) -> Tuple[int, str]:
3479+
import shutil, subprocess, os, platform
3480+
if not platform.system() == "Linux":
3481+
raise Exception("This feature should only be used on Linux, if you see this error there is no TK fallback implemented in the code.")
3482+
zenity_bin = shutil.which("zenity")
3483+
if not zenity_bin:
3484+
zenity_bin = shutil.which("yad")
3485+
if not zenity_bin:
3486+
raise Exception("Zenity not present")
3487+
3488+
# Build args based on keywords
3489+
args = ['/usr/bin/env', zenity_bin, '--'+typ]
3490+
for k, v in kwargs.items():
3491+
if v is True:
3492+
args.append(f'--{k.replace("_", "-").strip("-")}')
3493+
elif isinstance(v, str):
3494+
cv = zenity_clean(v) if k != "title" else v
3495+
args.append(f'--{k.replace("_", "-").strip("-")}={cv}')
3496+
3497+
# Build filetypes specially if specified
3498+
if filetypes:
3499+
for name, globs in filetypes:
3500+
if name:
3501+
globlist = globs.split()
3502+
args.append(f'--file-filter={name.replace("|", "")} ({", ".join(t for t in globlist)})|{globs}')
3503+
3504+
# Default filename and folder
3505+
if initialdir is None:
3506+
initialdir=os.getcwd()
3507+
if initialfile is None:
3508+
initialfile=""
3509+
initialpath = os.path.join(initialdir, initialfile)
3510+
args.append(f'--filename={initialpath}')
3511+
3512+
clean_env = os.environ.copy()
3513+
clean_env.pop("LD_LIBRARY_PATH", None)
3514+
clean_env["PATH"] = "/usr/bin:/bin"
3515+
3516+
proc = subprocess.Popen(
3517+
args,
3518+
stdout=subprocess.PIPE,
3519+
stderr=subprocess.DEVNULL,
3520+
shell=False,
3521+
env=clean_env
3522+
)
3523+
stdout, _ = proc.communicate()
3524+
3525+
return (proc.returncode, stdout.decode('utf-8').strip())
3526+
3527+
# note: In this section we wrap around file dialogues to allow for zenity
3528+
3529+
def zentk_askopenfilename(**options):
3530+
try:
3531+
result = zenity('file-selection', filetypes=options.get("filetypes"), initialdir=options.get("initialdir"), title=options.get("title"))[1]
3532+
except:
3533+
from tkinter.filedialog import askopenfilename
3534+
result = askopenfilename(**options)
3535+
return result
3536+
3537+
def zentk_askopenmultiplefilenames(**options):
3538+
try:
3539+
from os.path import isfile
3540+
files = zenity('file-selection', filetypes=options.get("filetypes"), initialdir=options.get("initialdir"), title=options.get("title"), multiple=True, separator="\n")[1].splitlines()
3541+
result = tuple(filter(isfile, files))
3542+
except:
3543+
from tkinter.filedialog import askopenfilenames
3544+
result = askopenfilenames(**options)
3545+
return result
3546+
3547+
def zentk_askdirectory(**options):
3548+
try:
3549+
result = zenity('file-selection', initialdir=options.get("initialdir"), title=options.get("title"), directory=True)[1]
3550+
except:
3551+
from tkinter.filedialog import askdirectory
3552+
result = askdirectory(**options)
3553+
return result
3554+
3555+
def zentk_asksaveasfilename(**options):
3556+
try:
3557+
result = zenity('file-selection', filetypes=options.get("filetypes"), initialdir=options.get("initialdir"), initialfile=options.get("initialfile"), title=options.get("title"), save=True)[1]
3558+
except:
3559+
from tkinter.filedialog import asksaveasfilename
3560+
result = asksaveasfilename(**options)
3561+
return result
3562+
### End of MIT license
3563+
34583564
# note: customtkinter-5.2.0
34593565
def show_gui():
34603566
global using_gui_launcher
34613567
using_gui_launcher = True
3462-
from tkinter.filedialog import askopenfilename, askdirectory
3463-
from tkinter.filedialog import asksaveasfilename
34643568

34653569
# if args received, launch
34663570
if len(sys.argv) != 1 and not args.showgui:
34673571
import tkinter as tk
34683572
root = tk.Tk() #we dont want the useless window to be visible, but we want it in taskbar
34693573
root.attributes("-alpha", 0)
3470-
args.model_param = askopenfilename(title="Select ggml model .bin or .gguf file or .kcpps config")
3574+
args.model_param = zentk_askopenfilename(title="Select ggml model .bin or .gguf file or .kcpps config")
34713575
root.withdraw()
34723576
root.quit()
34733577
if args.model_param and args.model_param!="" and (args.model_param.lower().endswith('.kcpps') or args.model_param.lower().endswith('.kcppt') or args.model_param.lower().endswith('.kcpps?download=true') or args.model_param.lower().endswith('.kcppt?download=true')):
@@ -3770,16 +3874,16 @@ def getfilename(var, text):
37703874
initialDir = initialDir if os.path.isdir(initialDir) else None
37713875
fnam = None
37723876
if dialog_type==2:
3773-
fnam = askdirectory(title=text, mustexist=True, initialdir=initialDir)
3877+
fnam = zentk_askdirectory(title=text, mustexist=True, initialdir=initialDir)
37743878
elif dialog_type==1:
3775-
fnam = asksaveasfilename(title=text, filetypes=filetypes, defaultextension=filetypes, initialdir=initialDir)
3879+
fnam = zentk_asksaveasfilename(title=text, filetypes=filetypes, defaultextension=filetypes, initialdir=initialDir)
37763880
if not fnam:
37773881
fnam = ""
37783882
else:
37793883
fnam = str(fnam).strip()
37803884
fnam = f"{fnam}.jsondb" if ".jsondb" not in fnam.lower() else fnam
37813885
else:
3782-
fnam = askopenfilename(title=text,filetypes=filetypes, initialdir=initialDir)
3886+
fnam = zentk_askopenfilename(title=text,filetypes=filetypes, initialdir=initialDir)
37833887
if fnam:
37843888
var.set(fnam)
37853889
if onchoosefile:
@@ -4181,7 +4285,7 @@ def togglerope(a,b,c):
41814285
def pickpremadetemplate():
41824286
initialDir = os.path.join(os.path.abspath(os.path.dirname(__file__)), 'kcpp_adapters')
41834287
initialDir = initialDir if os.path.isdir(initialDir) else None
4184-
fnam = askopenfilename(title="Pick Premade ChatCompletions Adapter",filetypes=[("JSON Adapter", "*.json")], initialdir=initialDir)
4288+
fnam = zentk_askopenfilename(title="Pick Premade ChatCompletions Adapter",filetypes=[("JSON Adapter", "*.json")], initialdir=initialDir)
41854289
if fnam:
41864290
chatcompletionsadapter_var.set(fnam)
41874291
ctk.CTkButton(model_tab, 64, text="Pick Premade", command=pickpremadetemplate).grid(row=25, column=0, padx=322, stick="nw")
@@ -4311,7 +4415,7 @@ def kcpp_export_template():
43114415
file_type = [("KoboldCpp LaunchTemplate", "*.kcppt")]
43124416
#remove blacklisted fields
43134417
savdict = convert_args_to_template(savdict)
4314-
filename = asksaveasfilename(filetypes=file_type, defaultextension=".kcppt")
4418+
filename = zentk_asksaveasfilename(filetypes=file_type, defaultextension=".kcppt")
43154419
if not filename:
43164420
return
43174421
filenamestr = str(filename).strip()
@@ -4335,7 +4439,7 @@ def kcpp_export_template():
43354439
# launch
43364440
def guilaunch():
43374441
if model_var.get() == "" and sd_model_var.get() == "" and whisper_model_var.get() == "" and tts_model_var.get() == "" and embeddings_model_var.get() == "" and nomodel.get()!=1:
4338-
tmp = askopenfilename(title="Select ggml model .bin or .gguf file")
4442+
tmp = zentk_askopenfilename(title="Select ggml model .bin or .gguf file")
43394443
model_var.set(tmp)
43404444
nonlocal nextstate
43414445
nextstate = 1
@@ -4713,7 +4817,7 @@ def save_config_gui():
47134817
export_vars()
47144818
savdict = json.loads(json.dumps(args.__dict__))
47154819
file_type = [("KoboldCpp Settings", "*.kcpps")]
4716-
filename = asksaveasfilename(filetypes=file_type, defaultextension=".kcpps")
4820+
filename = zentk_asksaveasfilename(filetypes=file_type, defaultextension=".kcpps")
47174821
if not filename:
47184822
return
47194823
filenamestr = str(filename).strip()
@@ -4727,7 +4831,7 @@ def save_config_gui():
47274831
def load_config_gui(): #this is used to populate the GUI with a config file, whereas load_config_cli simply overwrites cli args
47284832
file_type = [("KoboldCpp Settings", "*.kcpps *.kcppt")]
47294833
global runmode_untouched
4730-
filename = askopenfilename(filetypes=file_type, defaultextension=".kcppt", initialdir=None)
4834+
filename = zentk_askopenfilename(filetypes=file_type, defaultextension=".kcppt", initialdir=None)
47314835
if not filename or filename=="":
47324836
return
47334837
runmode_untouched = False
@@ -5371,8 +5475,7 @@ def analyze_gguf_model(args,filename):
53715475
def analyze_gguf_model_wrapper(filename=""):
53725476
if not filename or filename=="":
53735477
try:
5374-
from tkinter.filedialog import askopenfilename
5375-
filename = askopenfilename(title="Select GGUF to analyze")
5478+
filename = zentk_askopenfilename(title="Select GGUF to analyze")
53765479
except Exception as e:
53775480
print(f"Cannot select file to analyze: {e}")
53785481
if not filename or filename=="" or not os.path.exists(filename):

0 commit comments

Comments
 (0)