Atlas-Install/source/Gui/TrackSelection.py

401 lines
No EOL
16 KiB
Python

import json
from tkinter import *
from tkinter import ttk
from tkinter import filedialog
class Orderbox(Listbox):
def order_up(self, *args, **kwargs):
self.order_change(delta_start=1)
def order_down(self, *args, **kwargs):
self.order_change(delta_end=1)
def order_change(self, delta_start: int = 0, delta_end: int = 0):
selection = self.curselection()
if len(selection) < 1: return
index = selection[0]
values = self.get(index - delta_start, index + delta_end)
if len(values) < 2: return
self.delete(index - delta_start, index + delta_end)
self.insert(index - delta_start, *reversed(values))
self.selection_set(index - delta_start + delta_end)
class TrackSelection:
def __init__(self, common):
self.common = common
self.root = Toplevel(self.common.gui_main.root)
self.root.title("Track selection")
self.root.iconbitmap("./icon.ico")
self.root.resizable(False, False)
self.root.grab_set()
self.text_is_equal_to = "is equal to"
self.text_is_in = "is in"
self.text_is_between = "is between"
self.text_contains = "contains"
self.text_and = "and"
self.text_nand = "nand"
self.text_or = "or"
self.text_nor = "nor"
self.text_xor = "xor"
self.text_xnor = "xnor"
self.condition_link_end = "end"
self.condition_links = {
self.text_and: lambda a, b: lambda track: a(track) and b(track),
self.text_nand: lambda a, b: lambda track: not (a(track) and b(track)),
self.text_or: lambda a, b: lambda track: a(track) or b(track),
self.text_nor: lambda a, b: lambda track: not (a(track) or b(track)),
self.text_xor: lambda a, b: lambda track: a(track) != b(track),
self.text_xnor: lambda a, b: lambda track: a(track) == b(track),
self.condition_link_end: -1
}
def get_change_enable_track_filter_func(root: [Frame, LabelFrame], frames_filter: list, variable_enable: BooleanVar):
def change_enable_track_filter(event: Event = None):
if variable_enable.get(): self.add_frame_track_filter(root=root, frames_filter=frames_filter)
else: self.del_frame_track_filter(frames_filter=frames_filter)
return change_enable_track_filter
self.track_sort = LabelFrame(self.root, text="Sort Track")
self.track_sort.grid(row=1, column=1, sticky="NEWS")
Label(self.track_sort, text="Sort track by : ").grid(row=1, column=1)
self.combobox_track_sort = ttk.Combobox(
self.track_sort,
values=list(self.common.ct_config.get_all_track_possibilities().keys())
)
self.combobox_track_sort.grid(row=1, column=2, sticky="NEWS")
self.combobox_track_sort.insert(END, self.common.ct_config.sort_track_attr)
self.track_filter = LabelFrame(self.root, text="Filter Track")
self.track_filter.grid(row=2, column=1, sticky="NEWS")
self.variable_enable_track_filter = BooleanVar(value=False)
self.frames_track_filter = []
self.checkbutton_track_filter = ttk.Checkbutton(
self.track_filter,
text="Enable track filter",
variable=self.variable_enable_track_filter,
command=get_change_enable_track_filter_func(
self.track_filter,
self.frames_track_filter,
self.variable_enable_track_filter
)
)
self.checkbutton_track_filter.grid(row=1, column=1)
Label(
self.track_filter,
text="Warning : only unordered tracks are affected by this option.",
fg="gray"
).grid(row=2, column=1)
self.track_highlight = LabelFrame(self.root, text="Highlight Track")
self.track_highlight.grid(row=3, column=1, sticky="NEWS")
self.variable_enable_track_highlight = BooleanVar(value=False)
self.frames_track_highlight = []
self.checkbutton_track_highlight = ttk.Checkbutton(
self.track_highlight,
text="Enable track highlight",
variable=self.variable_enable_track_highlight,
command=get_change_enable_track_filter_func(
self.track_highlight,
self.frames_track_highlight,
self.variable_enable_track_highlight
)
)
self.checkbutton_track_highlight.grid(row=1, column=1)
self.track_random_new = LabelFrame(self.root, text="Overwrite random cup new")
self.track_random_new.grid(row=4, column=1, sticky="NEWS")
self.variable_enable_track_random_new = BooleanVar(value=False)
self.frames_track_random_new = []
self.checkbutton_track_random_new = ttk.Checkbutton(
self.track_random_new,
text="Enable overwriting random \"new\" track",
variable=self.variable_enable_track_random_new,
command=get_change_enable_track_filter_func(
self.track_random_new,
self.frames_track_random_new,
self.variable_enable_track_random_new
)
)
self.checkbutton_track_random_new.grid(row=1, column=1)
self.all_frames_filters = {
"track_filter": {
"frame": self.track_filter,
"variable_checkbox": self.variable_enable_track_filter,
"list": self.frames_track_filter
},
"track_highlight": {
"frame": self.track_highlight,
"variable_checkbox": self.variable_enable_track_highlight,
"list": self.frames_track_highlight
},
"track_random_new": {
"frame": self.track_random_new,
"variable_checkbox": self.variable_enable_track_random_new,
"list": self.frames_track_random_new
}
}
if self.common.json_frame_filter: self.load_from_json(self.common.json_frame_filter)
self.frame_action_button = Frame(self.root)
self.frame_action_button.grid(row=100, column=1, sticky="E")
Button(
self.frame_action_button,
text="Apply change",
relief=RIDGE,
command=self.apply_configuration
).grid(row=1, column=1, sticky="W")
Button(
self.frame_action_button,
text="Save to file",
relief=RIDGE,
command=self.promp_save_to_file
).grid(row=1, column=2, sticky="E")
Button(
self.frame_action_button,
text="Load from file",
relief=RIDGE,
command=self.promp_load_from_file
).grid(row=1, column=3, sticky="E")
def del_frame_track_filter(self, frames_filter: list, index: int = 0):
for elem in frames_filter[index:]: # remove all track filter after this one
elem["frame"].destroy()
del frames_filter[index:]
def add_frame_track_filter(self, root: [Frame, LabelFrame], frames_filter: list):
index = len(frames_filter) - 1
frame = Frame(root)
frame.grid(row=len(frames_filter) + 10, column=1, sticky="NEWS")
Label(frame, text="If track's").grid(row=1, column=1)
track_property = ttk.Combobox(frame, values=list(self.common.ct_config.get_all_track_possibilities().keys()))
track_property.current(0)
track_property.grid(row=1, column=2)
frame_equal = Frame(frame)
entry_equal = Entry(frame_equal, width=20)
entry_equal.grid(row=1, column=1)
entry_equal.insert(END, "value")
frame_in = Frame(frame)
entry_in = Entry(frame_in, width=30)
entry_in.grid(row=1, column=1)
entry_in.insert(END, "value1, value2, ...")
frame_between = Frame(frame)
entry_start = Entry(frame_between, width=10)
entry_start.grid(row=1, column=1)
entry_start.insert(END, "value1")
Label(frame_between, text="and").grid(row=1, column=2)
entry_end = Entry(frame_between, width=10)
entry_end.insert(END, "value2")
entry_end.grid(row=1, column=3)
frame_contains = Frame(frame)
entry_contains = Entry(frame_contains, width=20)
entry_contains.grid(row=1, column=1)
entry_contains.insert(END, "value")
condition_frames = {
self.text_is_equal_to: frame_equal,
self.text_is_in: frame_in,
self.text_is_between: frame_between,
self.text_contains: frame_contains,
}
def change_condition_type(event: Event = None):
condition = combobox_condition_type.get()
for frame in condition_frames.values(): frame.grid_forget()
condition_frames[condition].grid(row=1, column=10)
combobox_condition_type = ttk.Combobox(frame, values=list(condition_frames.keys()), width=10)
combobox_condition_type.current(0)
combobox_condition_type.bind("<<ComboboxSelected>>", change_condition_type)
change_condition_type()
combobox_condition_type.grid(row=1, column=3)
def change_condition_link(event: Event = None):
link = next_condition_link.get()
if link == self.condition_link_end:
self.del_frame_track_filter(frames_filter, index=index + 1)
else:
if frames_filter[-1]["frame"] == frame: # if this is the last filter available
self.add_frame_track_filter(root=root, frames_filter=frames_filter)
next_condition_link = ttk.Combobox(frame, values=list(self.condition_links.keys()), width=10)
next_condition_link.bind("<<ComboboxSelected>>", change_condition_link)
next_condition_link.set(self.condition_link_end)
next_condition_link.grid(row=1, column=100)
frames_filter.append({
"frame": frame,
"track_property": track_property,
"condition_type": combobox_condition_type,
"value_equal": entry_equal,
"value_in": entry_in,
"value_between_start": entry_start,
"value_between_end": entry_end,
"value_contains": entry_contains,
"next_condition_link": next_condition_link
})
def apply_configuration(self):
self.common.gui_main.is_track_configuration_edited = True
self.common.ct_config.sort_track_attr = self.combobox_track_sort.get()
self.common.ct_config.filter_track_selection = self.get_filter(
self.variable_enable_track_filter,
self.frames_track_filter
)
self.common.ct_config.filter_track_highlight = self.get_filter(
self.variable_enable_track_highlight,
self.frames_track_highlight
)
self.common.ct_config.filter_track_random_new = self.get_filter(
self.variable_enable_track_random_new,
self.frames_track_random_new
)
self.common.json_frame_filter = self.save_to_json()
self.root.destroy()
def get_filter(self, condition_enabled: BooleanVar, frames_filter: list):
s = lambda x: str(x).strip()
filter_condition = lambda track: True
if not condition_enabled.get(): return filter_condition
next_condition_link_func = lambda a, b: lambda track: a(track) and b(track)
for frame_filter in frames_filter:
track_property = frame_filter["track_property"].get()
value_equal = frame_filter["value_equal"].get()
value_in = frame_filter["value_in"].get()
value_contains = frame_filter["value_contains"].get()
value_between_start = frame_filter["value_between_start"].get()
value_between_end = frame_filter["value_between_end"].get()
def _is_between_func_wrapper(property):
def _is_between_func(track):
from_ = s(value_between_start)
to = s(value_between_end)
prop = s(getattr(track, property, None))
if from_.isnumeric() and prop.isnumeric() and to.isnumeric(): return int(from_) <= int(prop) <= int(to)
else: return from_ <= prop <= to
return _is_between_func
track_conditions_filter = {
self.text_is_equal_to: lambda property: lambda track:
s(getattr(track, property, None)) == s(value_equal),
self.text_is_in: lambda property: lambda track:
s(getattr(track, property, None)) in [s(v) for v in value_in.split(",")],
self.text_is_between:
_is_between_func_wrapper,
self.text_contains: lambda property: lambda track:
s(value_contains) in s(getattr(track, property, None))
}
track_condition_type = frame_filter["condition_type"].get()
track_condition_filter = track_conditions_filter[track_condition_type]
filter_condition = next_condition_link_func(
filter_condition,
track_condition_filter(track_property)
)
next_condition_link = frame_filter["next_condition_link"].get()
next_condition_link_func = self.condition_links[next_condition_link]
return filter_condition
def save_to_json(self) -> dict:
"""
save the menu values in a dictionnary
:return: dictionnary
"""
json_frame_filter = {}
for frame_name, tk_frames_data in self.all_frames_filters.items():
json_frame_filter[frame_name] = []
for tk_data in tk_frames_data["list"]:
json_frame_filter[frame_name].append({})
for varname, tkvar in tk_data.items():
if "get" in dir(tkvar):
json_frame_filter[frame_name][-1][varname] = tkvar.get()
return json_frame_filter
def save_to_file(self, path: str):
with open(path, "w", encoding="utf8") as f:
json.dump(self.save_to_json(), f, ensure_ascii=False)
def promp_save_to_file(self):
filename = filedialog.asksaveasfilename(
title="Save track configuration",
defaultextension=".mkwf.tc",
filetypes=[("track configuration (*.mkwf.tc)", "*.mkwf.tc")]
)
if filename: self.save_to_file(filename)
def load_from_json(self, json_frame_filter: dict) -> None:
"""
:param json_frame_filter: json to load in the menu
"""
for json_filter_name, json_frames in json_frame_filter.items():
tk_frame_filter = self.all_frames_filters[json_filter_name]
self.del_frame_track_filter(tk_frame_filter["list"])
tk_frame_filter["variable_checkbox"].set(len(json_frames) > 0)
for i, json_frame in enumerate(json_frames):
self.add_frame_track_filter(
self.all_frames_filters[json_filter_name]["frame"],
self.all_frames_filters[json_filter_name]["list"]
)
for json_param_name, json_param_value in json_frame.items():
obj = self.all_frames_filters[json_filter_name]["list"][i][json_param_name]
if type(obj) in [Entry]:
obj.delete(0, END)
obj.insert(0, json_param_value)
else:
obj.set(json_param_value)
def load_from_file(self, path: str) -> None:
with open(path, encoding="utf8") as f:
self.load_from_json(json.load(f))
def promp_load_from_file(self):
filename = filedialog.askopenfilename(
title="Load track configuration",
defaultextension=".mkwf.tc",
filetypes=[("track configuration (*.mkwf.tc)", "*.mkwf.tc")]
)
if filename: self.load_from_file(filename)