mirror of
https://github.com/Anjok07/ultimatevocalremovergui.git
synced 2024-11-28 01:10:56 +01:00
Revert "Feature resolution type"
This commit is contained in:
parent
43dd4f48b4
commit
cce49e16b2
3
.gitignore
vendored
3
.gitignore
vendored
@ -3,5 +3,4 @@ data.pkl
|
|||||||
# Testing models
|
# Testing models
|
||||||
model_iter26_sr33075_hl384_w512.pth
|
model_iter26_sr33075_hl384_w512.pth
|
||||||
baseline.pth
|
baseline.pth
|
||||||
temp.wav
|
temp.wav
|
||||||
models/**/*.pth
|
|
@ -61,8 +61,6 @@ DEFAULT_DATA = {
|
|||||||
'modelInstrumentalLabel': '',
|
'modelInstrumentalLabel': '',
|
||||||
'modelStackedLabel': '',
|
'modelStackedLabel': '',
|
||||||
'aiModel': 'v4',
|
'aiModel': 'v4',
|
||||||
'resType': 'Kaiser Fast',
|
|
||||||
'manType': False,
|
|
||||||
|
|
||||||
'useModel': 'instrumental',
|
'useModel': 'instrumental',
|
||||||
'lastDir': None,
|
'lastDir': None,
|
||||||
@ -242,15 +240,15 @@ class MainWindow(TkinterDnD.Tk):
|
|||||||
# Layout
|
# Layout
|
||||||
IMAGE_HEIGHT = 140
|
IMAGE_HEIGHT = 140
|
||||||
FILEPATHS_HEIGHT = 90
|
FILEPATHS_HEIGHT = 90
|
||||||
OPTIONS_HEIGHT = 285
|
OPTIONS_HEIGHT = 240
|
||||||
CONVERSIONBUTTON_HEIGHT = 35
|
CONVERSIONBUTTON_HEIGHT = 35
|
||||||
COMMAND_HEIGHT = 200
|
COMMAND_HEIGHT = 200
|
||||||
PROGRESS_HEIGHT = 26
|
PROGRESS_HEIGHT = 26
|
||||||
PADDING = 10
|
PADDING = 10
|
||||||
|
|
||||||
COL1_ROWS = 9.5
|
COL1_ROWS = 8
|
||||||
COL2_ROWS = 8.1
|
COL2_ROWS = 7
|
||||||
COL3_ROWS = 5.5
|
COL3_ROWS = 5
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
# Run the __init__ method on the tk.Tk class
|
# Run the __init__ method on the tk.Tk class
|
||||||
@ -307,10 +305,8 @@ class MainWindow(TkinterDnD.Tk):
|
|||||||
self.hopValue_var = tk.StringVar(value=data['hop_length'])
|
self.hopValue_var = tk.StringVar(value=data['hop_length'])
|
||||||
self.winSize_var = tk.StringVar(value=data['window_size'])
|
self.winSize_var = tk.StringVar(value=data['window_size'])
|
||||||
self.nfft_var = tk.StringVar(value=data['n_fft'])
|
self.nfft_var = tk.StringVar(value=data['n_fft'])
|
||||||
self.manType_var = tk.BooleanVar(value=data['manType'])
|
|
||||||
# AI model
|
# AI model
|
||||||
self.aiModel_var = tk.StringVar(value=data['aiModel'])
|
self.aiModel_var = tk.StringVar(value=data['aiModel'])
|
||||||
self.resType_var = tk.StringVar(value=data['resType'])
|
|
||||||
self.last_aiModel = self.aiModel_var.get()
|
self.last_aiModel = self.aiModel_var.get()
|
||||||
# Other
|
# Other
|
||||||
self.inputPathsEntry_var = tk.StringVar(value='')
|
self.inputPathsEntry_var = tk.StringVar(value='')
|
||||||
@ -402,6 +398,7 @@ class MainWindow(TkinterDnD.Tk):
|
|||||||
text='Save to',
|
text='Save to',
|
||||||
command=self.open_export_filedialog)
|
command=self.open_export_filedialog)
|
||||||
self.filePaths_saveTo_Entry = ttk.Entry(master=self.filePaths_Frame,
|
self.filePaths_saveTo_Entry = ttk.Entry(master=self.filePaths_Frame,
|
||||||
|
|
||||||
textvariable=self.exportPath_var,
|
textvariable=self.exportPath_var,
|
||||||
state=tk.DISABLED
|
state=tk.DISABLED
|
||||||
)
|
)
|
||||||
@ -470,11 +467,6 @@ class MainWindow(TkinterDnD.Tk):
|
|||||||
text='Model Test Mode',
|
text='Model Test Mode',
|
||||||
variable=self.modelFolder_var,
|
variable=self.modelFolder_var,
|
||||||
)
|
)
|
||||||
# Manual Constants
|
|
||||||
self.options_manType_Checkbutton = ttk.Checkbutton(master=self.options_Frame,
|
|
||||||
text='Custom parameters',
|
|
||||||
variable=self.manType_var,
|
|
||||||
)
|
|
||||||
# -Column 2-
|
# -Column 2-
|
||||||
# SR
|
# SR
|
||||||
self.options_sr_Entry = ttk.Entry(master=self.options_Frame,
|
self.options_sr_Entry = ttk.Entry(master=self.options_Frame,
|
||||||
@ -500,13 +492,6 @@ class MainWindow(TkinterDnD.Tk):
|
|||||||
self.options_nfft_Label = tk.Label(master=self.options_Frame,
|
self.options_nfft_Label = tk.Label(master=self.options_Frame,
|
||||||
text='N_FFT', anchor=tk.W,
|
text='N_FFT', anchor=tk.W,
|
||||||
background='#63605f', font=self.font, foreground='white', relief="sunken")
|
background='#63605f', font=self.font, foreground='white', relief="sunken")
|
||||||
# Resolution Type
|
|
||||||
self.options_resType_Label = tk.Label(master=self.options_Frame,
|
|
||||||
text='Resolution Type', anchor=tk.CENTER,
|
|
||||||
background='#63605f', font=self.font, foreground='white', relief="sunken")
|
|
||||||
self.options_resType_Optionmenu = ttk.OptionMenu(self.options_Frame,
|
|
||||||
self.resType_var,
|
|
||||||
None, 'Kaiser Fast', 'Kaiser Best', 'Scipy')
|
|
||||||
# AI model
|
# AI model
|
||||||
self.options_aiModel_Label = tk.Label(master=self.options_Frame,
|
self.options_aiModel_Label = tk.Label(master=self.options_Frame,
|
||||||
text='Choose AI Engine', anchor=tk.CENTER,
|
text='Choose AI Engine', anchor=tk.CENTER,
|
||||||
@ -557,9 +542,6 @@ class MainWindow(TkinterDnD.Tk):
|
|||||||
# Model Folder
|
# Model Folder
|
||||||
self.options_modelFolder_Checkbutton.place(x=0, y=0, width=0, height=0,
|
self.options_modelFolder_Checkbutton.place(x=0, y=0, width=0, height=0,
|
||||||
relx=0, rely=7/self.COL1_ROWS, relwidth=1/3, relheight=1/self.COL1_ROWS)
|
relx=0, rely=7/self.COL1_ROWS, relwidth=1/3, relheight=1/self.COL1_ROWS)
|
||||||
# Manual Constants
|
|
||||||
self.options_manType_Checkbutton.place(x=0, y=0, width=0, height=0,
|
|
||||||
relx=0, rely=8/self.COL1_ROWS, relwidth=1/3, relheight=1/self.COL1_ROWS)
|
|
||||||
# -Column 2-
|
# -Column 2-
|
||||||
# SR
|
# SR
|
||||||
self.options_sr_Label.place(x=5, y=4, width=5, height=-8,
|
self.options_sr_Label.place(x=5, y=4, width=5, height=-8,
|
||||||
@ -581,16 +563,11 @@ class MainWindow(TkinterDnD.Tk):
|
|||||||
relx=1/3, rely=3/self.COL2_ROWS, relwidth=1/3/2, relheight=1/self.COL2_ROWS)
|
relx=1/3, rely=3/self.COL2_ROWS, relwidth=1/3/2, relheight=1/self.COL2_ROWS)
|
||||||
self.options_nfft_Entry.place(x=15, y=4, width=5, height=-8,
|
self.options_nfft_Entry.place(x=15, y=4, width=5, height=-8,
|
||||||
relx=1/3 + 1/3/2, rely=3/self.COL2_ROWS, relwidth=1/3/4, relheight=1/self.COL2_ROWS)
|
relx=1/3 + 1/3/2, rely=3/self.COL2_ROWS, relwidth=1/3/4, relheight=1/self.COL2_ROWS)
|
||||||
# Resolution Type
|
|
||||||
self.options_resType_Label.place(x=5, y=8, width=-30, height=-8,
|
|
||||||
relx=1/3, rely=4/self.COL2_ROWS, relwidth=1/3, relheight=1/self.COL2_ROWS)
|
|
||||||
self.options_resType_Optionmenu.place(x=5, y=8, width=-30, height=-8,
|
|
||||||
relx=1/3, rely=5/self.COL2_ROWS, relwidth=1/3, relheight=1/self.COL2_ROWS)
|
|
||||||
# AI model
|
# AI model
|
||||||
self.options_aiModel_Label.place(x=5, y=8, width=-30, height=-8,
|
self.options_aiModel_Label.place(x=5, y=-5, width=-30, height=-8,
|
||||||
relx=1/3, rely=6/self.COL2_ROWS, relwidth=1/3, relheight=1/self.COL2_ROWS)
|
relx=1/3, rely=5/self.COL2_ROWS, relwidth=1/3, relheight=1/self.COL2_ROWS)
|
||||||
self.options_aiModel_Optionmenu.place(x=5, y=8, width=-30, height=-8,
|
self.options_aiModel_Optionmenu.place(x=5, y=-5, width=-30, height=-8,
|
||||||
relx=1/3, rely=7/self.COL2_ROWS, relwidth=1/3, relheight=1/self.COL2_ROWS)
|
relx=1/3, rely=6/self.COL2_ROWS, relwidth=1/3, relheight=1/self.COL2_ROWS)
|
||||||
|
|
||||||
# -Column 3-
|
# -Column 3-
|
||||||
# Choose Model
|
# Choose Model
|
||||||
@ -615,8 +592,6 @@ class MainWindow(TkinterDnD.Tk):
|
|||||||
lambda *args: self.decode_modelNames())
|
lambda *args: self.decode_modelNames())
|
||||||
self.stackedModel_var.trace_add('write',
|
self.stackedModel_var.trace_add('write',
|
||||||
lambda *args: self.decode_modelNames())
|
lambda *args: self.decode_modelNames())
|
||||||
self.manType_var.trace_add('write',
|
|
||||||
lambda *args: self.decode_modelNames())
|
|
||||||
# Model deselect
|
# Model deselect
|
||||||
self.aiModel_var.trace_add('write',
|
self.aiModel_var.trace_add('write',
|
||||||
lambda *args: self.deselect_models())
|
lambda *args: self.deselect_models())
|
||||||
@ -666,13 +641,11 @@ class MainWindow(TkinterDnD.Tk):
|
|||||||
input_paths = self.inputPaths
|
input_paths = self.inputPaths
|
||||||
instrumentalModel_path = self.instrumentalLabel_to_path[self.instrumentalModel_var.get()] # nopep8
|
instrumentalModel_path = self.instrumentalLabel_to_path[self.instrumentalModel_var.get()] # nopep8
|
||||||
stackedModel_path = self.stackedLabel_to_path[self.stackedModel_var.get()] # nopep8
|
stackedModel_path = self.stackedLabel_to_path[self.stackedModel_var.get()] # nopep8
|
||||||
resType = self.resType_var.get().lower().replace(' ', '_')
|
|
||||||
# Get constants
|
# Get constants
|
||||||
instrumental = get_model_values(self.instrumentalModel_var.get())
|
instrumental = get_model_values(self.instrumentalModel_var.get())
|
||||||
stacked = get_model_values(self.stackedModel_var.get())
|
stacked = get_model_values(self.stackedModel_var.get())
|
||||||
try:
|
try:
|
||||||
if ([bool(instrumental), bool(stacked)].count(True) == 2 and
|
if [bool(instrumental), bool(stacked)].count(True) == 2:
|
||||||
not self.manType_var.get()):
|
|
||||||
sr = DEFAULT_DATA['sr']
|
sr = DEFAULT_DATA['sr']
|
||||||
hop_length = DEFAULT_DATA['hop_length']
|
hop_length = DEFAULT_DATA['hop_length']
|
||||||
window_size = DEFAULT_DATA['window_size']
|
window_size = DEFAULT_DATA['window_size']
|
||||||
@ -755,10 +728,6 @@ class MainWindow(TkinterDnD.Tk):
|
|||||||
'hop_length': hop_length,
|
'hop_length': hop_length,
|
||||||
'window_size': window_size,
|
'window_size': window_size,
|
||||||
'n_fft': n_fft, # not needed for v2
|
'n_fft': n_fft, # not needed for v2
|
||||||
# Resolution Type
|
|
||||||
'resType': resType,
|
|
||||||
# Parsed constants should be fixed
|
|
||||||
'manType': self.manType_var.get(),
|
|
||||||
# Other Variables (Tkinter)
|
# Other Variables (Tkinter)
|
||||||
'window': self,
|
'window': self,
|
||||||
'text_widget': self.command_Text,
|
'text_widget': self.command_Text,
|
||||||
@ -791,14 +760,7 @@ class MainWindow(TkinterDnD.Tk):
|
|||||||
|
|
||||||
# Loop through each constant (key) and its widgets
|
# Loop through each constant (key) and its widgets
|
||||||
for key, (widget, var) in widgetsVars.items():
|
for key, (widget, var) in widgetsVars.items():
|
||||||
if self.manType_var.get():
|
if stacked_selectable:
|
||||||
if str(widget.cget('state')) != 'normal':
|
|
||||||
# Manual typing and widget not enabled
|
|
||||||
widget.configure(state=tk.NORMAL)
|
|
||||||
if '/' in str(var.get()):
|
|
||||||
var.set(var.get().split('/')[0])
|
|
||||||
continue
|
|
||||||
elif stacked_selectable:
|
|
||||||
if instrumental_selectable:
|
if instrumental_selectable:
|
||||||
if (key in instrumental.keys() and
|
if (key in instrumental.keys() and
|
||||||
key in stacked.keys()):
|
key in stacked.keys()):
|
||||||
@ -806,17 +768,19 @@ class MainWindow(TkinterDnD.Tk):
|
|||||||
widget.configure(state=tk.DISABLED)
|
widget.configure(state=tk.DISABLED)
|
||||||
var.set('%d/%d' % (instrumental[key], stacked[key]))
|
var.set('%d/%d' % (instrumental[key], stacked[key]))
|
||||||
continue
|
continue
|
||||||
elif key in stacked.keys():
|
else:
|
||||||
# Only stacked selectable
|
if key in stacked.keys():
|
||||||
widget.configure(state=tk.DISABLED)
|
# Only stacked selectable
|
||||||
var.set(stacked[key])
|
widget.configure(state=tk.DISABLED)
|
||||||
continue
|
var.set(stacked[key])
|
||||||
elif (key in instrumental.keys() and
|
continue
|
||||||
instrumental_selectable):
|
else:
|
||||||
# Stacked model can not be selected
|
# Stacked model can not be selected
|
||||||
widget.configure(state=tk.DISABLED)
|
if (key in instrumental.keys() and
|
||||||
var.set(instrumental[key])
|
instrumental_selectable):
|
||||||
continue
|
widget.configure(state=tk.DISABLED)
|
||||||
|
var.set(instrumental[key])
|
||||||
|
continue
|
||||||
|
|
||||||
# If widget is already enabled, no need to reset the value
|
# If widget is already enabled, no need to reset the value
|
||||||
if str(widget.cget('state')) != 'normal':
|
if str(widget.cget('state')) != 'normal':
|
||||||
@ -979,8 +943,7 @@ class MainWindow(TkinterDnD.Tk):
|
|||||||
# Get constants
|
# Get constants
|
||||||
instrumental = get_model_values(self.instrumentalModel_var.get())
|
instrumental = get_model_values(self.instrumentalModel_var.get())
|
||||||
stacked = get_model_values(self.stackedModel_var.get())
|
stacked = get_model_values(self.stackedModel_var.get())
|
||||||
if ([bool(instrumental), bool(stacked)].count(True) == 2 and
|
if [bool(instrumental), bool(stacked)].count(True) == 2:
|
||||||
not self.manType_var.get()):
|
|
||||||
sr = DEFAULT_DATA['sr']
|
sr = DEFAULT_DATA['sr']
|
||||||
hop_length = DEFAULT_DATA['hop_length']
|
hop_length = DEFAULT_DATA['hop_length']
|
||||||
window_size = DEFAULT_DATA['window_size']
|
window_size = DEFAULT_DATA['window_size']
|
||||||
@ -1013,8 +976,6 @@ class MainWindow(TkinterDnD.Tk):
|
|||||||
'modelInstrumentalLabel': self.instrumentalModel_var.get(),
|
'modelInstrumentalLabel': self.instrumentalModel_var.get(),
|
||||||
'modelStackedLabel': self.stackedModel_var.get(),
|
'modelStackedLabel': self.stackedModel_var.get(),
|
||||||
'aiModel': self.aiModel_var.get(),
|
'aiModel': self.aiModel_var.get(),
|
||||||
'resType': self.resType_var.get(),
|
|
||||||
'manType': self.manType_var.get(),
|
|
||||||
})
|
})
|
||||||
|
|
||||||
self.destroy()
|
self.destroy()
|
||||||
|
@ -53,10 +53,6 @@ data = {
|
|||||||
'hop_length': 1_024,
|
'hop_length': 1_024,
|
||||||
'window_size': 320,
|
'window_size': 320,
|
||||||
'n_fft': 2_048,
|
'n_fft': 2_048,
|
||||||
# Resolution Type
|
|
||||||
'resType': 'kaiser_fast',
|
|
||||||
# Parsed constants should be fixed
|
|
||||||
'manType': False,
|
|
||||||
}
|
}
|
||||||
default_sr = data['sr']
|
default_sr = data['sr']
|
||||||
default_hop_length = data['hop_length']
|
default_hop_length = data['hop_length']
|
||||||
@ -96,10 +92,6 @@ def update_constants(model_name):
|
|||||||
data['window_size'] = default_window_size
|
data['window_size'] = default_window_size
|
||||||
data['n_fft'] = default_n_fft
|
data['n_fft'] = default_n_fft
|
||||||
|
|
||||||
if data['manType']:
|
|
||||||
# Default constants should be fixed
|
|
||||||
return
|
|
||||||
|
|
||||||
for text_part in text_parts:
|
for text_part in text_parts:
|
||||||
if 'sr' in text_part:
|
if 'sr' in text_part:
|
||||||
text_part = text_part.replace('sr', '')
|
text_part = text_part.replace('sr', '')
|
||||||
@ -214,7 +206,7 @@ def main(window: tk.Wm, text_widget: tk.Text, button_widget: tk.Button, progress
|
|||||||
data['sr'],
|
data['sr'],
|
||||||
False,
|
False,
|
||||||
dtype=np.float32,
|
dtype=np.float32,
|
||||||
res_type=data['resType'])
|
res_type='kaiser_fast')
|
||||||
|
|
||||||
return X, sr
|
return X, sr
|
||||||
|
|
||||||
@ -377,8 +369,6 @@ def main(window: tk.Wm, text_widget: tk.Text, button_widget: tk.Button, progress
|
|||||||
folder_path = os.path.join(data["export_path"], modelFolderName)
|
folder_path = os.path.join(data["export_path"], modelFolderName)
|
||||||
if not os.path.isdir(folder_path):
|
if not os.path.isdir(folder_path):
|
||||||
os.mkdir(folder_path)
|
os.mkdir(folder_path)
|
||||||
else:
|
|
||||||
folder_path = ''
|
|
||||||
|
|
||||||
# Determine Loops
|
# Determine Loops
|
||||||
total_loops = data['stackPasses']
|
total_loops = data['stackPasses']
|
||||||
@ -386,9 +376,10 @@ def main(window: tk.Wm, text_widget: tk.Text, button_widget: tk.Button, progress
|
|||||||
total_loops += 1
|
total_loops += 1
|
||||||
|
|
||||||
for file_num, music_file in enumerate(data['input_paths'], start=1):
|
for file_num, music_file in enumerate(data['input_paths'], start=1):
|
||||||
# Determine File Name
|
|
||||||
base_name = os.path.join(folder_path, f'{file_num}_{os.path.splitext(os.path.basename(music_file))[0]}')
|
|
||||||
try:
|
try:
|
||||||
|
# Determine File Name
|
||||||
|
base_name = os.path.join(folder_path, f'{file_num}_{os.path.splitext(os.path.basename(music_file))[0]}')
|
||||||
|
|
||||||
for loop_num in range(total_loops):
|
for loop_num in range(total_loops):
|
||||||
# -Determine which model will be used-
|
# -Determine which model will be used-
|
||||||
if not loop_num:
|
if not loop_num:
|
||||||
|
@ -175,10 +175,6 @@ data = {
|
|||||||
'hop_length': 1_024,
|
'hop_length': 1_024,
|
||||||
'window_size': 320,
|
'window_size': 320,
|
||||||
'n_fft': 2_048,
|
'n_fft': 2_048,
|
||||||
# Resolution Type
|
|
||||||
'resType': 'kaiser_fast',
|
|
||||||
# Parsed constants should be fixed
|
|
||||||
'manType': False,
|
|
||||||
}
|
}
|
||||||
default_sr = data['sr']
|
default_sr = data['sr']
|
||||||
default_hop_length = data['hop_length']
|
default_hop_length = data['hop_length']
|
||||||
@ -216,10 +212,6 @@ def update_constants(model_name):
|
|||||||
data['window_size'] = default_window_size
|
data['window_size'] = default_window_size
|
||||||
data['n_fft'] = default_n_fft
|
data['n_fft'] = default_n_fft
|
||||||
|
|
||||||
if data['manType']:
|
|
||||||
# Default constants should be fixed
|
|
||||||
return
|
|
||||||
|
|
||||||
for text_part in text_parts:
|
for text_part in text_parts:
|
||||||
if 'sr' in text_part:
|
if 'sr' in text_part:
|
||||||
text_part = text_part.replace('sr', '')
|
text_part = text_part.replace('sr', '')
|
||||||
@ -384,17 +376,16 @@ def main(window: tk.Wm, text_widget: tk.Text, button_widget: tk.Button, progress
|
|||||||
folder_path = os.path.join(data["export_path"], modelFolderName)
|
folder_path = os.path.join(data["export_path"], modelFolderName)
|
||||||
if not os.path.isdir(folder_path):
|
if not os.path.isdir(folder_path):
|
||||||
os.mkdir(folder_path)
|
os.mkdir(folder_path)
|
||||||
else:
|
|
||||||
folder_path = ''
|
|
||||||
|
|
||||||
# Determine Loops
|
# Determine Loops
|
||||||
total_loops = data['stackPasses']
|
total_loops = data['stackPasses']
|
||||||
if not data['stackOnly']:
|
if not data['stackOnly']:
|
||||||
total_loops += 1
|
total_loops += 1
|
||||||
for file_num, music_file in enumerate(data['input_paths'], start=1):
|
for file_num, music_file in enumerate(data['input_paths'], start=1):
|
||||||
# Determine File Name
|
|
||||||
base_name = os.path.join(folder_path, f'{file_num}_{os.path.splitext(os.path.basename(music_file))[0]}')
|
|
||||||
try:
|
try:
|
||||||
|
# Determine File Name
|
||||||
|
base_name = os.path.join(folder_path, f'{file_num}_{os.path.splitext(os.path.basename(music_file))[0]}')
|
||||||
|
|
||||||
# --Seperate Music Files--
|
# --Seperate Music Files--
|
||||||
for loop_num in range(total_loops):
|
for loop_num in range(total_loops):
|
||||||
# -Determine which model will be used-
|
# -Determine which model will be used-
|
||||||
@ -437,7 +428,7 @@ def main(window: tk.Wm, text_widget: tk.Text, button_widget: tk.Button, progress
|
|||||||
# Wave source
|
# Wave source
|
||||||
text_widget.write(base_text + 'Loading wave source...\n')
|
text_widget.write(base_text + 'Loading wave source...\n')
|
||||||
X, sr = librosa.load(music_file, data['sr'], False,
|
X, sr = librosa.load(music_file, data['sr'], False,
|
||||||
dtype=np.float32, res_type=data['resType'])
|
dtype=np.float32, res_type='kaiser_fast')
|
||||||
if X.ndim == 1:
|
if X.ndim == 1:
|
||||||
X = np.asarray([X, X])
|
X = np.asarray([X, X])
|
||||||
text_widget.write(base_text + 'Done!\n')
|
text_widget.write(base_text + 'Done!\n')
|
||||||
|
@ -0,0 +1 @@
|
|||||||
|
Models Go Here
|
@ -0,0 +1 @@
|
|||||||
|
Models Go Here
|
@ -0,0 +1 @@
|
|||||||
|
Models Go Here
|
@ -0,0 +1 @@
|
|||||||
|
Models Go Here
|
Loading…
Reference in New Issue
Block a user