diff --git a/Input Helper.py b/Input Helper.py new file mode 100644 index 0000000..9505ab2 --- /dev/null +++ b/Input Helper.py @@ -0,0 +1,102 @@ +import customtkinter +import json +from MCE.custom_widgets.ctk_scrollable_dropdown import CTkScrollableDropdown +import os +from tkinter import END + +class InputHelper(customtkinter.CTk): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.create_tabview() + self.create_invite_student_widgets() + self.create_lessons_widgets() + + + def create_tabview(self): + self.tabview = customtkinter.CTkTabview(master=self) + self.tabview.grid(row=0, column=0) + + self.cafe_tab = self.tabview.add("Cafe") # add tab at the end + self.lessons_tab = self.tabview.add("Lessons") # add tab at the end + self.tabview.set("Cafe") # set currently visible + + def create_invite_student_widgets(self): + self.invitation_label = customtkinter.CTkLabel(master=self.cafe_tab, text="Copy and paste this in AAS Invitation Settings:") + self.invitation_label.grid(row=0, column=0, padx=60) + + self.invitation_entry = customtkinter.CTkEntry(master=self.cafe_tab, width=500) + self.invitation_entry.grid(row=1, column=0) + + self.invite_copy_button = customtkinter.CTkButton(master=self.cafe_tab, text="Copy", width=40, command=lambda : self.copy_entry(self.invitation_entry, self.invite_copy_button)) + self.invite_copy_button.grid(row=1, column=3, padx=5) + + self.invite_clear_button = customtkinter.CTkButton(master=self.cafe_tab, text="Clear", width=40, fg_color="crimson", command=lambda : self.invitation_entry.delete(0, END)) + self.invite_clear_button.grid(row=1, column=4, padx=5) + + self.invite_frame = customtkinter.CTkFrame(master=self.cafe_tab, fg_color="transparent") + self.invite_frame.grid(row=2, column=0, padx=20, pady=20) + + self.server_dropdown = customtkinter.CTkOptionMenu(master=self.invite_frame, values=self.find_json_files("MCE/student_list"), command=self.switch_server, width=40) + self.server_dropdown.grid(row=0, column=0) + + self.student_entry = customtkinter.CTkComboBox(master=self.invite_frame, width=300) + self.student_entry.grid(row=0, column=1, padx=(50,0)) + + self.student_dropdown = CTkScrollableDropdown(self.student_entry, width=300, height=550, autocomplete=True, command=lambda choice: self.insert(choice, self.invitation_entry), values=[""]) + self.server_dropdown.set("EN") + self.switch_server("EN") + + def create_lessons_widgets(self): + self.lessons_label = customtkinter.CTkLabel(master=self.lessons_tab, text="Copy and paste this in AAS Lessons Settings:") + self.lessons_label.grid(row=0, column=0, padx=60) + + self.lessons_entry = customtkinter.CTkEntry(master=self.lessons_tab, width=500) + self.lessons_entry.grid(row=1, column=0) + + self.lessons_copy_button = customtkinter.CTkButton(master=self.lessons_tab, text="Copy", width=40, command=lambda : self.copy_entry(self.lessons_entry, self.lessons_copy_button)) + self.lessons_copy_button.grid(row=1, column=1, padx=5) + + self.lessons_clear_button = customtkinter.CTkButton(master=self.lessons_tab, text="Clear", width=40, fg_color="crimson", command=lambda : self.lessons_entry.delete(0, END)) + self.lessons_clear_button.grid(row=1, column=2, padx=5) + + self.lessons_buttons_frame = customtkinter.CTkFrame(master=self.lessons_tab, fg_color="transparent") + self.lessons_buttons_frame.grid(row=2, column=0, padx=20, pady=20) + + for i in range(9): + self.lesson_button = customtkinter.CTkButton(master=self.lessons_buttons_frame, text=str(i+1), command=lambda choice=str(i+1): self.insert(choice, self.lessons_entry), width=40) + self.lesson_button.grid(row=0, column=i, padx=5) + + + def find_json_files(self,folder_path): + json_files = [] + for root, dirs, files in os.walk(folder_path): + for file in files: + if file.endswith(".json"): + json_files.append(os.path.splitext(file)[0]) + return json_files + + def switch_server(self, server): + with open(f"MCE/student_list/{server}.json", "r") as f: + student_list = json.load(f) + self.student_dropdown.configure(values=student_list) + + def insert(self, value, entry): + entry.insert(index=END, string=value + " > ") + + def copy_entry(self, entry, button): + text_to_copy = entry.get() + + # Check if there is text to copy + if text_to_copy: + # Clear the clipboard and set the new text + self.clipboard_clear() + self.clipboard_append(text_to_copy) + self.update() # This is necessary on some systems to update the clipboard + button_color = button.cget("fg_color") + button.configure(fg_color="green") + self.after(2000, lambda : button.configure(fg_color=['#3B8ED0', '#1F6AA5'])) + +if __name__ == "__main__": + app = InputHelper() + app.title("Input Helper") + app.mainloop() diff --git a/MCE/custom_widgets/ctk_scrollable_dropdown.py b/MCE/custom_widgets/ctk_scrollable_dropdown.py new file mode 100644 index 0000000..212e969 --- /dev/null +++ b/MCE/custom_widgets/ctk_scrollable_dropdown.py @@ -0,0 +1,337 @@ +''' +Advanced Scrollable Dropdown class for customtkinter widgets +Author: Akash Bora +''' + +import customtkinter +import sys +import time +import difflib + +class CTkScrollableDropdown(customtkinter.CTkToplevel): + + def __init__(self, attach, x=None, y=None, button_color=None, height: int = 200, width: int = None, + fg_color=None, button_height: int = 20, justify="center", scrollbar_button_color=None, + scrollbar=True, scrollbar_button_hover_color=None, frame_border_width=2, values=[], + command=None, image_values=[], alpha: float = 0.97, frame_corner_radius=20, double_click=False, + resize=True, frame_border_color=None, text_color=None, autocomplete=False, **button_kwargs): + + super().__init__(takefocus=1) + + self.focus() + self.lift() + self.alpha = alpha + self.attach = attach + self.corner = frame_corner_radius + self.padding = 0 + self.focus_something = False + self.disable = True + self.update() + + if sys.platform.startswith("win"): + self.after(100, lambda: self.overrideredirect(True)) + self.transparent_color = self._apply_appearance_mode(self._fg_color) + self.attributes("-transparentcolor", self.transparent_color) + elif sys.platform.startswith("darwin"): + self.overrideredirect(True) + self.transparent_color = 'systemTransparent' + self.attributes("-transparent", True) + self.focus_something = True + else: + self.overrideredirect(True) + self.transparent_color = '#000001' + self.corner = 0 + self.padding = 18 + self.withdraw() + + self.hide = True + self.attach.bind('', lambda e: self._withdraw() if not self.disable else None, add="+") + self.attach.winfo_toplevel().bind('', lambda e: self._withdraw() if not self.disable else None, add="+") + self.attach.winfo_toplevel().bind("", lambda e: self._withdraw() if not self.disable else None, add="+") + + + self.attributes('-alpha', 0) + self.disable = False + self.fg_color = customtkinter.ThemeManager.theme["CTkFrame"]["fg_color"] if fg_color is None else fg_color + self.scroll_button_color = customtkinter.ThemeManager.theme["CTkScrollbar"]["button_color"] if scrollbar_button_color is None else scrollbar_button_color + self.scroll_hover_color = customtkinter.ThemeManager.theme["CTkScrollbar"]["button_hover_color"] if scrollbar_button_hover_color is None else scrollbar_button_hover_color + self.frame_border_color = customtkinter.ThemeManager.theme["CTkFrame"]["border_color"] if frame_border_color is None else frame_border_color + self.button_color = customtkinter.ThemeManager.theme["CTkFrame"]["top_fg_color"] if button_color is None else button_color + self.text_color = customtkinter.ThemeManager.theme["CTkLabel"]["text_color"] if text_color is None else text_color + + if scrollbar is False: + self.scroll_button_color = self.fg_color + self.scroll_hover_color = self.fg_color + + self.frame = customtkinter.CTkScrollableFrame(self, bg_color=self.transparent_color, fg_color=self.fg_color, + scrollbar_button_hover_color=self.scroll_hover_color, + corner_radius=self.corner, border_width=frame_border_width, + scrollbar_button_color=self.scroll_button_color, + border_color=self.frame_border_color) + self.frame._scrollbar.grid_configure(padx=3) + self.frame.pack(expand=True, fill="both") + self.dummy_entry = customtkinter.CTkEntry(self.frame, fg_color="transparent", border_width=0, height=1, width=1) + self.no_match = customtkinter.CTkLabel(self.frame, text="No Match") + self.height = height + self.height_new = height + self.width = width + self.command = command + self.fade = False + self.resize = resize + self.autocomplete = autocomplete + self.var_update = customtkinter.StringVar() + self.appear = False + + if justify.lower()=="left": + self.justify = "w" + elif justify.lower()=="right": + self.justify = "e" + else: + self.justify = "c" + + self.button_height = button_height + self.values = values + self.button_num = len(self.values) + self.image_values = None if len(image_values)!=len(self.values) else image_values + + self.resizable(width=False, height=False) + self.transient(self.master) + self._init_buttons(**button_kwargs) + + # Add binding for different ctk widgets + if double_click or self.attach.winfo_name().startswith("!ctkentry") or self.attach.winfo_name().startswith("!ctkcombobox"): + self.attach.bind('', lambda e: self._iconify(), add="+") + else: + self.attach.bind('', lambda e: self._iconify(), add="+") + + if self.attach.winfo_name().startswith("!ctkcombobox"): + self.attach._canvas.tag_bind("right_parts", "", lambda e: self._iconify()) + self.attach._canvas.tag_bind("dropdown_arrow", "", lambda e: self._iconify()) + if self.command is None: + self.command = self.attach.set + + if self.attach.winfo_name().startswith("!ctkoptionmenu"): + self.attach._canvas.bind("", lambda e: self._iconify()) + self.attach._text_label.bind("", lambda e: self._iconify()) + if self.command is None: + self.command = self.attach.set + + self.attach.bind("", lambda _: self._destroy(), add="+") + + self.update_idletasks() + self.x = x + self.y = y + + if self.autocomplete: + self.bind_autocomplete() + + self.deiconify() + self.withdraw() + + self.attributes("-alpha", self.alpha) + + def _destroy(self): + self.after(500, self.destroy_popup) + + def _withdraw(self): + if self.winfo_viewable() and self.hide: + self.withdraw() + + self.event_generate("<>") + self.hide = True + + def _update(self, a, b, c): + self.live_update(self.attach._entry.get()) + + def bind_autocomplete(self, ): + def appear(x): + self.appear = True + + if self.attach.winfo_name().startswith("!ctkcombobox"): + self.attach._entry.configure(textvariable=self.var_update) + self.attach._entry.bind("", appear) + self.attach.set(self.values[0]) + self.var_update.trace_add('write', self._update) + + if self.attach.winfo_name().startswith("!ctkentry"): + self.attach.configure(textvariable=self.var_update) + self.attach.bind("", appear) + self.var_update.trace_add('write', self._update) + + def fade_out(self): + for i in range(100,0,-10): + if not self.winfo_exists(): + break + self.attributes("-alpha", i/100) + self.update() + time.sleep(1/100) + + def fade_in(self): + for i in range(0,100,10): + if not self.winfo_exists(): + break + self.attributes("-alpha", i/100) + self.update() + time.sleep(1/100) + + def _init_buttons(self, **button_kwargs): + self.i = 0 + self.widgets = {} + for row in self.values: + self.widgets[self.i] = customtkinter.CTkButton(self.frame, + text=row, + height=self.button_height, + fg_color=self.button_color, + text_color=self.text_color, + image=self.image_values[i] if self.image_values is not None else None, + anchor=self.justify, + command=lambda k=row: self._attach_key_press(k), **button_kwargs) + self.widgets[self.i].pack(fill="x", pady=2, padx=(self.padding, 0)) + self.i+=1 + + self.hide = False + + def destroy_popup(self): + self.destroy() + self.disable = True + + def place_dropdown(self): + self.x_pos = self.attach.winfo_rootx() if self.x is None else self.x + self.attach.winfo_rootx() + self.y_pos = self.attach.winfo_rooty() + self.attach.winfo_reqheight() + 5 if self.y is None else self.y + self.attach.winfo_rooty() + self.width_new = self.attach.winfo_width() if self.width is None else self.width + + if self.resize: + if self.button_num<=5: + self.height_new = self.button_height * self.button_num + 55 + else: + self.height_new = self.button_height * self.button_num + 35 + if self.height_new>self.height: + self.height_new = self.height + + self.geometry('{}x{}+{}+{}'.format(self.width_new, self.height_new, + self.x_pos, self.y_pos)) + self.fade_in() + self.attributes('-alpha', self.alpha) + self.attach.focus() + + def _iconify(self): + if self.disable: return + if self.hide: + self.event_generate("<>") + self._deiconify() + self.focus() + self.hide = False + self.place_dropdown() + if self.focus_something: + self.dummy_entry.pack() + self.dummy_entry.focus_set() + self.after(100, self.dummy_entry.pack_forget) + else: + self.withdraw() + self.hide = True + + def _attach_key_press(self, k): + self.event_generate("<>") + self.fade = True + if self.command: + self.command(k) + self.fade = False + self.fade_out() + self.withdraw() + self.hide = True + + def live_update(self, string=None): + if not self.appear: return + if self.disable: return + if self.fade: return + if string: + string = string.lower() + self._deiconify() + i=1 + for key in self.widgets.keys(): + s = self.widgets[key].cget("text").lower() + text_similarity = difflib.SequenceMatcher(None, s[0:len(string)], string).ratio() + similar = s.startswith(string) or text_similarity > 0.75 + if not similar: + self.widgets[key].pack_forget() + else: + self.widgets[key].pack(fill="x", pady=2, padx=(self.padding, 0)) + i+=1 + + if i==1: + self.no_match.pack(fill="x", pady=2, padx=(self.padding, 0)) + else: + self.no_match.pack_forget() + self.button_num = i + self.place_dropdown() + + else: + self.no_match.pack_forget() + self.button_num = len(self.values) + for key in self.widgets.keys(): + self.widgets[key].destroy() + self._init_buttons() + self.place_dropdown() + + self.frame._parent_canvas.yview_moveto(0.0) + self.appear = False + + def insert(self, value, **kwargs): + self.widgets[self.i] = customtkinter.CTkButton(self.frame, + text=value, + height=self.button_height, + fg_color=self.button_color, + text_color=self.text_color, + anchor=self.justify, + command=lambda k=value: self._attach_key_press(k), **kwargs) + self.widgets[self.i].pack(fill="x", pady=2, padx=(self.padding, 0)) + self.i+=1 + self.values.append(value) + + def _deiconify(self): + if len(self.values)>0: + self.deiconify() + + def popup(self, x=None, y=None): + self.x = x + self.y = y + self.hide = True + self._iconify() + + def configure(self, **kwargs): + if "height" in kwargs: + self.height = kwargs.pop("height") + self.height_new = self.height + + if "alpha" in kwargs: + self.alpha = kwargs.pop("alpha") + + if "width" in kwargs: + self.width = kwargs.pop("width") + + if "fg_color" in kwargs: + self.frame.configure(fg_color=kwargs.pop("fg_color")) + + if "values" in kwargs: + self.values = kwargs.pop("values") + self.image_values = None + self.button_num = len(self.values) + for key in self.widgets.keys(): + self.widgets[key].destroy() + self._init_buttons() + + if "image_values" in kwargs: + self.image_values = kwargs.pop("image_values") + self.image_values = None if len(self.image_values)!=len(self.values) else self.image_values + if self.image_values is not None: + i=0 + for key in self.widgets.keys(): + self.widgets[key].configure(image=self.image_values[i]) + i+=1 + + if "button_color" in kwargs: + for key in self.widgets.keys(): + self.widgets[key].configure(fg_color=kwargs.pop("button_color")) + + for key in self.widgets.keys(): + self.widgets[key].configure(**kwargs) diff --git a/MCE/custom_widgets/ctk_scrollable_dropdown_frame.py b/MCE/custom_widgets/ctk_scrollable_dropdown_frame.py new file mode 100644 index 0000000..609cf0c --- /dev/null +++ b/MCE/custom_widgets/ctk_scrollable_dropdown_frame.py @@ -0,0 +1,291 @@ +''' +Advanced Scrollable Dropdown Frame class for customtkinter widgets +Author: Akash Bora +''' + +import customtkinter +import sys +import difflib + +class CTkScrollableDropdownFrame(customtkinter.CTkFrame): + + def __init__(self, attach, x=None, y=None, button_color=None, height: int = 200, width: int = None, + fg_color=None, button_height: int = 20, justify="center", scrollbar_button_color=None, + scrollbar=True, scrollbar_button_hover_color=None, frame_border_width=2, values=[], + command=None, image_values=[], double_click=False, frame_corner_radius=True, resize=True, frame_border_color=None, + text_color=None, autocomplete=False, **button_kwargs): + + super().__init__(master=attach.winfo_toplevel(), bg_color=attach.cget("bg_color")) + + self.attach = attach + self.corner = 11 if frame_corner_radius else 0 + self.padding = 0 + self.disable = True + + self.hide = True + self.attach.bind('', lambda e: self._withdraw() if not self.disable else None, add="+") + self.attach.winfo_toplevel().bind("", lambda e: self._withdraw() if not self.disable else None, add="+") + + self.disable = False + self.fg_color = customtkinter.ThemeManager.theme["CTkFrame"]["fg_color"] if fg_color is None else fg_color + self.scroll_button_color = customtkinter.ThemeManager.theme["CTkScrollbar"]["button_color"] if scrollbar_button_color is None else scrollbar_button_color + self.scroll_hover_color = customtkinter.ThemeManager.theme["CTkScrollbar"]["button_hover_color"] if scrollbar_button_hover_color is None else scrollbar_button_hover_color + self.frame_border_color = customtkinter.ThemeManager.theme["CTkFrame"]["border_color"] if frame_border_color is None else frame_border_color + self.button_color = customtkinter.ThemeManager.theme["CTkFrame"]["top_fg_color"] if button_color is None else button_color + self.text_color = customtkinter.ThemeManager.theme["CTkLabel"]["text_color"] if text_color is None else text_color + + if scrollbar is False: + self.scroll_button_color = self.fg_color + self.scroll_hover_color = self.fg_color + + self.frame = customtkinter.CTkScrollableFrame(self, fg_color=self.fg_color, bg_color=attach.cget("bg_color"), + scrollbar_button_hover_color=self.scroll_hover_color, + corner_radius=self.corner, border_width=frame_border_width, + scrollbar_button_color=self.scroll_button_color, + border_color=self.frame_border_color) + self.frame._scrollbar.grid_configure(padx=3) + self.frame.pack(expand=True, fill="both") + + if self.corner==0: + self.corner = 21 + + self.dummy_entry = customtkinter.CTkEntry(self.frame, fg_color="transparent", border_width=0, height=1, width=1) + self.no_match = customtkinter.CTkLabel(self.frame, text="No Match") + self.height = height + self.height_new = height + self.width = width + self.command = command + self.fade = False + self.resize = resize + self.autocomplete = autocomplete + self.var_update = customtkinter.StringVar() + self.appear = False + + if justify.lower()=="left": + self.justify = "w" + elif justify.lower()=="right": + self.justify = "e" + else: + self.justify = "c" + + self.button_height = button_height + self.values = values + self.button_num = len(self.values) + self.image_values = None if len(image_values)!=len(self.values) else image_values + + self._init_buttons(**button_kwargs) + + # Add binding for different ctk widgets + if double_click or self.attach.winfo_name().startswith("!ctkentry") or self.attach.winfo_name().startswith("!ctkcombobox"): + self.attach.bind('', lambda e: self._iconify(), add="+") + self.attach._entry.bind('', lambda e: self._withdraw() if not self.disable else None, add="+") + else: + self.attach.bind('', lambda e: self._iconify(), add="+") + + if self.attach.winfo_name().startswith("!ctkcombobox"): + self.attach._canvas.tag_bind("right_parts", "", lambda e: self._iconify()) + self.attach._canvas.tag_bind("dropdown_arrow", "", lambda e: self._iconify()) + + if self.command is None: + self.command = self.attach.set + + if self.attach.winfo_name().startswith("!ctkoptionmenu"): + self.attach._canvas.bind("", lambda e: self._iconify()) + self.attach._text_label.bind("", lambda e: self._iconify()) + if self.command is None: + self.command = self.attach.set + + self.x = x + self.y = y + + self.attach.bind("", lambda _: self._destroy(), add="+") + + if self.autocomplete: + self.bind_autocomplete() + + def _destroy(self): + self.after(500, self.destroy_popup) + + def _withdraw(self): + if self.winfo_viewable() and self.hide: + self.place_forget() + + self.event_generate("<>") + self.hide = True + + def _update(self, a, b, c): + self.live_update(self.attach._entry.get()) + + def bind_autocomplete(self, ): + def appear(x): + self.appear = True + + if self.attach.winfo_name().startswith("!ctkcombobox"): + self.attach._entry.configure(textvariable=self.var_update) + self.attach.set(self.values[0]) + self.attach._entry.bind("", appear) + self.var_update.trace_add('write', self._update) + + if self.attach.winfo_name().startswith("!ctkentry"): + self.attach.configure(textvariable=self.var_update) + self.attach.bind("", appear) + self.var_update.trace_add('write', self._update) + + def _init_buttons(self, **button_kwargs): + self.i = 0 + self.widgets = {} + for row in self.values: + self.widgets[self.i] = customtkinter.CTkButton(self.frame, + text=row, + height=self.button_height, + fg_color=self.button_color, + text_color=self.text_color, + image=self.image_values[i] if self.image_values is not None else None, + anchor=self.justify, + command=lambda k=row: self._attach_key_press(k), **button_kwargs) + self.widgets[self.i].pack(fill="x", pady=2, padx=(self.padding, 0)) + self.i+=1 + + self.hide = False + + def destroy_popup(self): + self.destroy() + self.disable = True + + def place_dropdown(self): + self.x_pos = self.attach.winfo_x() if self.x is None else self.x + self.attach.winfo_rootx() + self.y_pos = self.attach.winfo_y() + self.attach.winfo_reqheight() + 5 if self.y is None else self.y + self.attach.winfo_rooty() + self.width_new = self.attach.winfo_width()-45+self.corner if self.width is None else self.width + + if self.resize: + if self.button_num<=5: + self.height_new = self.button_height * self.button_num + 55 + else: + self.height_new = self.button_height * self.button_num + 35 + if self.height_new>self.height: + self.height_new = self.height + + self.frame.configure(width=self.width_new, height=self.height_new) + self.place(x=self.x_pos, y=self.y_pos) + + if sys.platform.startswith("darwin"): + self.dummy_entry.pack() + self.after(100, self.dummy_entry.pack_forget()) + + self.lift() + self.attach.focus() + + def _iconify(self): + if self.disable: return + if self.hide: + self.event_generate("<>") + self.hide = False + self.place_dropdown() + else: + self.place_forget() + self.hide = True + + def _attach_key_press(self, k): + self.event_generate("<>") + self.fade = True + if self.command: + self.command(k) + self.fade = False + self.place_forget() + self.hide = True + + def live_update(self, string=None): + if not self.appear: return + if self.disable: return + if self.fade: return + if string: + string = string.lower() + self._deiconify() + i=1 + for key in self.widgets.keys(): + s = self.widgets[key].cget("text").lower() + text_similarity = difflib.SequenceMatcher(None, s[0:len(string)], string).ratio() + similar = s.startswith(string) or text_similarity > 0.75 + if not similar: + self.widgets[key].pack_forget() + else: + self.widgets[key].pack(fill="x", pady=2, padx=(self.padding, 0)) + i+=1 + + if i==1: + self.no_match.pack(fill="x", pady=2, padx=(self.padding, 0)) + else: + self.no_match.pack_forget() + self.button_num = i + self.place_dropdown() + + else: + self.no_match.pack_forget() + self.button_num = len(self.values) + for key in self.widgets.keys(): + self.widgets[key].destroy() + self._init_buttons() + self.place_dropdown() + + self.frame._parent_canvas.yview_moveto(0.0) + self.appear = False + + def insert(self, value, **kwargs): + self.widgets[self.i] = customtkinter.CTkButton(self.frame, + text=value, + height=self.button_height, + fg_color=self.button_color, + text_color=self.text_color, + anchor=self.justify, + command=lambda k=value: self._attach_key_press(k), **kwargs) + self.widgets[self.i].pack(fill="x", pady=2, padx=(self.padding, 0)) + self.i+=1 + self.values.append(value) + + def _deiconify(self): + if len(self.values)>0: + self.pack_forget() + + def popup(self, x=None, y=None): + self.x = x + self.y = y + self.hide = True + self._iconify() + + def configure(self, **kwargs): + if "height" in kwargs: + self.height = kwargs.pop("height") + self.height_new = self.height + + if "alpha" in kwargs: + self.alpha = kwargs.pop("alpha") + + if "width" in kwargs: + self.width = kwargs.pop("width") + + if "fg_color" in kwargs: + self.frame.configure(fg_color=kwargs.pop("fg_color")) + + if "values" in kwargs: + self.values = kwargs.pop("values") + self.image_values = None + self.button_num = len(self.values) + for key in self.widgets.keys(): + self.widgets[key].destroy() + self._init_buttons() + + if "image_values" in kwargs: + self.image_values = kwargs.pop("image_values") + self.image_values = None if len(self.image_values)!=len(self.values) else self.image_values + if self.image_values is not None: + i=0 + for key in self.widgets.keys(): + self.widgets[key].configure(image=self.image_values[i]) + i+=1 + + if "button_color" in kwargs: + for key in self.widgets.keys(): + self.widgets[key].configure(fg_color=kwargs.pop("button_color")) + + for key in self.widgets.keys(): + self.widgets[key].configure(**kwargs) diff --git a/MCE/student_list/EN.json b/MCE/student_list/EN.json new file mode 100644 index 0000000..9facb11 --- /dev/null +++ b/MCE/student_list/EN.json @@ -0,0 +1,158 @@ +[ + "Airi", + "Akane", + "Akane (Bunny Girl)", + "Akari", + "Ako", + "Arisu", + "Arisu (Maid)", + "Aru", + "Aru (New Year)", + "Asuna", + "Asuna (Bunny Girl)", + "Atsuko", + "Ayane", + "Ayane (Swimsuit)", + "Azusa", + "Azusa (Swimsuit)", + "Cherino", + "Cherino (Hot Spring)", + "Chihiro", + "Chinatsu", + "Chinatsu (Hot Spring)", + "Chise", + "Chise (Swimsuit)", + "Eimi", + "Fubuki", + "Fuuka", + "Fuuka (New Year)", + "Hanae", + "Hanae (Christmas)", + "Hanako", + "Hanako (Swimsuit)", + "Hare", + "Haruka", + "Haruka (New Year)", + "Haruna", + "Haruna (New Year)", + "Haruna (Sportswear)", + "Hasumi", + "Hasumi (Sportswear)", + "Hatsune Miku", + "Hibiki", + "Hibiki (Cheerleader)", + "Hifumi", + "Hifumi (Swimsuit)", + "Himari", + "Hina", + "Hina (Swimsuit)", + "Hinata", + "Hinata (Swimsuit)", + "Hiyori", + "Hoshino", + "Hoshino (Swimsuit)", + "Iori", + "Iori (Swimsuit)", + "Iroha", + "Izumi", + "Izumi (Swimsuit)", + "Izuna", + "Izuna (Swimsuit)", + "Junko", + "Junko (New Year)", + "Juri", + "Kaede", + "Kaho", + "Kanna", + "Karin", + "Karin (Bunny Girl)", + "Kayoko", + "Kayoko (New Year)", + "Kazusa", + "Kirino", + "Koharu", + "Koharu (Swimsuit)", + "Kokona", + "Kotama", + "Kotori", + "Kotori (Cheerleader)", + "Koyuki", + "Maki", + "Mari", + "Mari (Sportswear)", + "Marina", + "Mashiro", + "Mashiro (Swimsuit)", + "Megu", + "Meru", + "Michiru", + "Midori", + "Mika", + "Mimori", + "Mimori (Swimsuit)", + "Mina", + "Mine", + "Minori", + "Misaki", + "Miyako", + "Miyako (Swimsuit)", + "Miyu", + "Miyu (Swimsuit)", + "Moe", + "Momiji", + "Momoi", + "Mutsuki", + "Mutsuki (New Year)", + "Nagisa", + "Natsu", + "Neru", + "Neru (Bunny Girl)", + "Noa", + "Nodoka", + "Nodoka (Hot Spring)", + "Nonomi", + "Nonomi (Swimsuit)", + "Pina", + "Reisa", + "Rumi", + "Saki", + "Saki (Swimsuit)", + "Sakurako", + "Saori", + "Saya", + "Saya (Casual)", + "Sena", + "Serika", + "Serika (New Year)", + "Serina", + "Serina (Christmas)", + "Shigure", + "Shimiko", + "Shiroko", + "Shiroko (Riding)", + "Shiroko (Swimsuit)", + "Shizuko", + "Shizuko (Swimsuit)", + "Shun", + "Shun (Kid)", + "Sumire", + "Suzumi", + "Toki", + "Toki (Bunny Girl)", + "Tomoe", + "Tsubaki", + "Tsukuyo", + "Tsurugi", + "Tsurugi (Swimsuit)", + "Ui", + "Ui (Swimsuit)", + "Utaha", + "Utaha (Cheerleader)", + "Wakamo", + "Wakamo (Swimsuit)", + "Yoshimi", + "Yuuka", + "Yuuka (Sportswear)", + "Yuzu", + "Yuzu (Maid)" +] \ No newline at end of file diff --git a/MCE/student_list/JP.json b/MCE/student_list/JP.json new file mode 100644 index 0000000..b0a03f3 --- /dev/null +++ b/MCE/student_list/JP.json @@ -0,0 +1,168 @@ +[ + "\u30a2\u30a4\u30ea", + "\u30a2\u30ab\u30cd", + "\u30a2\u30ab\u30cd\n\uff08\u30d0\u30cb\u30fc\u30ac\u30fc\u30eb\uff09", + "\u30a2\u30ab\u30ea", + "\u30a2\u30b3", + "\u30a2\u30b9\u30ca", + "\u30a2\u30b9\u30ca\n\uff08\u30d0\u30cb\u30fc\u30ac\u30fc\u30eb\uff09", + "\u30a2\u30ba\u30b5", + "\u30a2\u30ba\u30b5\uff08\u6c34\u7740\uff09", + "\u30a2\u30c4\u30b3", + "\u30a2\u30e4\u30cd", + "\u30a2\u30e4\u30cd\uff08\u6c34\u7740\uff09", + "\u30a2\u30ea\u30b9", + "\u30a2\u30ea\u30b9\uff08\u30e1\u30a4\u30c9\uff09", + "\u30a2\u30eb", + "\u30a2\u30eb\uff08\u6b63\u6708\uff09", + "\u30a4\u30aa\u30ea", + "\u30a4\u30aa\u30ea\uff08\u6c34\u7740\uff09", + "\u30a4\u30ba\u30ca", + "\u30a4\u30ba\u30ca\uff08\u6c34\u7740\uff09", + "\u30a4\u30ba\u30df", + "\u30a4\u30ba\u30df\uff08\u6c34\u7740\uff09", + "\u30a4\u30c1\u30ab", + "\u30a4\u30ed\u30cf", + "\u30a6\u30a4", + "\u30a6\u30a4\uff08\u6c34\u7740\uff09", + "\u30a6\u30bf\u30cf", + "\u30a6\u30bf\u30cf\uff08\u5fdc\u63f4\u56e3\uff09", + "\u30a8\u30a4\u30df", + "\u30a8\u30a4\u30df\uff08\u6c34\u7740\uff09", + "\u30ab\u30a8\u30c7", + "\u30ab\u30b9\u30df", + "\u30ab\u30ba\u30b5", + "\u30ab\u30db", + "\u30ab\u30e8\u30b3", + "\u30ab\u30e8\u30b3\uff08\u6b63\u6708\uff09", + "\u30ab\u30ea\u30f3", + "\u30ab\u30ea\u30f3\n\uff08\u30d0\u30cb\u30fc\u30ac\u30fc\u30eb\uff09", + "\u30ab\u30f3\u30ca", + "\u30ad\u30ad\u30e7\u30a6", + "\u30ad\u30ea\u30ce", + "\u30b3\u30b3\u30ca", + "\u30b3\u30bf\u30de", + "\u30b3\u30c8\u30ea", + "\u30b3\u30c8\u30ea\uff08\u5fdc\u63f4\u56e3\uff09", + "\u30b3\u30cf\u30eb", + "\u30b3\u30cf\u30eb\uff08\u6c34\u7740\uff09", + "\u30b3\u30e6\u30ad", + "\u30b5\u30aa\u30ea", + "\u30b5\u30ad", + "\u30b5\u30ad\uff08\u6c34\u7740\uff09", + "\u30b5\u30af\u30e9\u30b3", + "\u30b5\u30e4", + "\u30b5\u30e4\uff08\u79c1\u670d\uff09", + "\u30b7\u30b0\u30ec", + "\u30b7\u30b0\u30ec\uff08\u6e29\u6cc9\uff09", + "\u30b7\u30ba\u30b3", + "\u30b7\u30ba\u30b3\uff08\u6c34\u7740\uff09", + "\u30b7\u30df\u30b3", + "\u30b7\u30e5\u30f3", + "\u30b7\u30e5\u30f3\uff08\u5e7c\u5973\uff09", + "\u30b7\u30ed\u30b3", + "\u30b7\u30ed\u30b3\n\uff08\u30e9\u30a4\u30c7\u30a3\u30f3\u30b0\uff09", + "\u30b7\u30ed\u30b3\uff08\u6c34\u7740\uff09", + "\u30b8\u30e5\u30ea", + "\u30b8\u30e5\u30f3\u30b3", + "\u30b8\u30e5\u30f3\u30b3\uff08\u6b63\u6708\uff09", + "\u30b9\u30ba\u30df", + "\u30b9\u30df\u30ec", + "\u30bb\u30ca", + "\u30bb\u30ea\u30ab", + "\u30bb\u30ea\u30ab\uff08\u6b63\u6708\uff09", + "\u30bb\u30ea\u30ca", + "\u30bb\u30ea\u30ca\n\uff08\u30af\u30ea\u30b9\u30de\u30b9\uff09", + "\u30c1\u30a7\u30ea\u30ce", + "\u30c1\u30a7\u30ea\u30ce\uff08\u6e29\u6cc9\uff09", + "\u30c1\u30bb", + "\u30c1\u30bb\uff08\u6c34\u7740\uff09", + "\u30c1\u30ca\u30c4", + "\u30c1\u30ca\u30c4\uff08\u6e29\u6cc9\uff09", + "\u30c1\u30d2\u30ed", + "\u30c4\u30af\u30e8", + "\u30c4\u30d0\u30ad", + "\u30c4\u30eb\u30ae", + "\u30c4\u30eb\u30ae\uff08\u6c34\u7740\uff09", + "\u30c8\u30ad", + "\u30c8\u30ad\n\uff08\u30d0\u30cb\u30fc\u30ac\u30fc\u30eb\uff09", + "\u30c8\u30e2\u30a8", + "\u30ca\u30ae\u30b5", + "\u30ca\u30c4", + "\u30cd\u30eb", + "\u30cd\u30eb\n\uff08\u30d0\u30cb\u30fc\u30ac\u30fc\u30eb\uff09", + "\u30ce\u30a2", + "\u30ce\u30c9\u30ab", + "\u30ce\u30c9\u30ab\uff08\u6e29\u6cc9\uff09", + "\u30ce\u30ce\u30df", + "\u30ce\u30ce\u30df\uff08\u6c34\u7740\uff09", + "\u30cf\u30b9\u30df", + "\u30cf\u30b9\u30df\uff08\u4f53\u64cd\u670d\uff09", + "\u30cf\u30ca\u30a8", + "\u30cf\u30ca\u30a8\n\uff08\u30af\u30ea\u30b9\u30de\u30b9\uff09", + "\u30cf\u30ca\u30b3", + "\u30cf\u30ca\u30b3\uff08\u6c34\u7740\uff09", + "\u30cf\u30eb\u30ab", + "\u30cf\u30eb\u30ab\uff08\u6b63\u6708\uff09", + "\u30cf\u30eb\u30ca", + "\u30cf\u30eb\u30ca\uff08\u4f53\u64cd\u670d\uff09", + "\u30cf\u30eb\u30ca\uff08\u6b63\u6708\uff09", + "\u30cf\u30ec", + "\u30d2\u30ca", + "\u30d2\u30ca\u30bf", + "\u30d2\u30ca\u30bf\uff08\u6c34\u7740\uff09", + "\u30d2\u30ca\uff08\u6c34\u7740\uff09", + "\u30d2\u30d3\u30ad", + "\u30d2\u30d3\u30ad\uff08\u5fdc\u63f4\u56e3\uff09", + "\u30d2\u30d5\u30df", + "\u30d2\u30d5\u30df\uff08\u6c34\u7740\uff09", + "\u30d2\u30de\u30ea", + "\u30d2\u30e8\u30ea", + "\u30d5\u30a3\u30fc\u30ca", + "\u30d5\u30a6\u30ab", + "\u30d5\u30a6\u30ab\uff08\u6b63\u6708\uff09", + "\u30d5\u30d6\u30ad", + "\u30db\u30b7\u30ce", + "\u30db\u30b7\u30ce\uff08\u6c34\u7740\uff09", + "\u30de\u30ad", + "\u30de\u30b7\u30ed", + "\u30de\u30b7\u30ed\uff08\u6c34\u7740\uff09", + "\u30de\u30ea\u30ca", + "\u30de\u30ea\u30fc", + "\u30de\u30ea\u30fc\uff08\u4f53\u64cd\u670d\uff09", + "\u30df\u30ab", + "\u30df\u30b5\u30ad", + "\u30df\u30c1\u30eb", + "\u30df\u30c9\u30ea", + "\u30df\u30ca", + "\u30df\u30cd", + "\u30df\u30ce\u30ea", + "\u30df\u30e2\u30ea", + "\u30df\u30e2\u30ea\uff08\u6c34\u7740\uff09", + "\u30df\u30e4\u30b3", + "\u30df\u30e4\u30b3\uff08\u6c34\u7740\uff09", + "\u30df\u30e6", + "\u30df\u30e6\uff08\u6c34\u7740\uff09", + "\u30e0\u30c4\u30ad", + "\u30e0\u30c4\u30ad\uff08\u6b63\u6708\uff09", + "\u30e1\u30b0", + "\u30e1\u30eb", + "\u30e2\u30a8", + "\u30e2\u30df\u30b8", + "\u30e2\u30e2\u30a4", + "\u30e6\u30a6\u30ab", + "\u30e6\u30a6\u30ab\uff08\u4f53\u64cd\u670d\uff09", + "\u30e6\u30ab\u30ea", + "\u30e6\u30ba", + "\u30e6\u30ba\uff08\u30e1\u30a4\u30c9\uff09", + "\u30e8\u30b7\u30df", + "\u30eb\u30df", + "\u30ec\u30a4\u30b5", + "\u30ec\u30f3\u30b2", + "\u30ef\u30ab\u30e2", + "\u30ef\u30ab\u30e2\uff08\u6c34\u7740\uff09", + "\u4f50\u5929\u6d99\u5b50", + "\u521d\u97f3\u30df\u30af", + "\u5fa1\u5742\u7f8e\u7434", + "\u98df\u8702\u64cd\u7948" +] \ No newline at end of file