2929import urllib .parse as urlparse
3030from concurrent .futures import ThreadPoolExecutor
3131from datetime import datetime , timezone
32+ from typing import Tuple
3233
3334# constants
3435sampler_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 ("<" , "<" )\
3470+ .replace (">" , ">" )\
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
34593565def 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):
53715475def 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