mirror of
https://github.com/TheFunny/ArisuAutoSweeper
synced 2025-12-16 19:55:12 +00:00
feat: input helper
Just a small program for users to quickly find students name.
This commit is contained in:
parent
151a085a9c
commit
93b7ca8cfc
102
Input Helper.py
Normal file
102
Input Helper.py
Normal file
@ -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()
|
||||||
337
MCE/custom_widgets/ctk_scrollable_dropdown.py
Normal file
337
MCE/custom_widgets/ctk_scrollable_dropdown.py
Normal file
@ -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('<Configure>', lambda e: self._withdraw() if not self.disable else None, add="+")
|
||||||
|
self.attach.winfo_toplevel().bind('<Configure>', lambda e: self._withdraw() if not self.disable else None, add="+")
|
||||||
|
self.attach.winfo_toplevel().bind("<ButtonPress>", 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('<Double-Button-1>', lambda e: self._iconify(), add="+")
|
||||||
|
else:
|
||||||
|
self.attach.bind('<Button-1>', lambda e: self._iconify(), add="+")
|
||||||
|
|
||||||
|
if self.attach.winfo_name().startswith("!ctkcombobox"):
|
||||||
|
self.attach._canvas.tag_bind("right_parts", "<Button-1>", lambda e: self._iconify())
|
||||||
|
self.attach._canvas.tag_bind("dropdown_arrow", "<Button-1>", 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("<Button-1>", lambda e: self._iconify())
|
||||||
|
self.attach._text_label.bind("<Button-1>", lambda e: self._iconify())
|
||||||
|
if self.command is None:
|
||||||
|
self.command = self.attach.set
|
||||||
|
|
||||||
|
self.attach.bind("<Destroy>", 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("<<Closed>>")
|
||||||
|
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("<Key>", 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("<Key>", 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("<<Opened>>")
|
||||||
|
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("<<Selected>>")
|
||||||
|
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)
|
||||||
291
MCE/custom_widgets/ctk_scrollable_dropdown_frame.py
Normal file
291
MCE/custom_widgets/ctk_scrollable_dropdown_frame.py
Normal file
@ -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('<Configure>', lambda e: self._withdraw() if not self.disable else None, add="+")
|
||||||
|
self.attach.winfo_toplevel().bind("<ButtonPress>", 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('<Double-Button-1>', lambda e: self._iconify(), add="+")
|
||||||
|
self.attach._entry.bind('<FocusOut>', lambda e: self._withdraw() if not self.disable else None, add="+")
|
||||||
|
else:
|
||||||
|
self.attach.bind('<Button-1>', lambda e: self._iconify(), add="+")
|
||||||
|
|
||||||
|
if self.attach.winfo_name().startswith("!ctkcombobox"):
|
||||||
|
self.attach._canvas.tag_bind("right_parts", "<Button-1>", lambda e: self._iconify())
|
||||||
|
self.attach._canvas.tag_bind("dropdown_arrow", "<Button-1>", 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("<Button-1>", lambda e: self._iconify())
|
||||||
|
self.attach._text_label.bind("<Button-1>", lambda e: self._iconify())
|
||||||
|
if self.command is None:
|
||||||
|
self.command = self.attach.set
|
||||||
|
|
||||||
|
self.x = x
|
||||||
|
self.y = y
|
||||||
|
|
||||||
|
self.attach.bind("<Destroy>", 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("<<Closed>>")
|
||||||
|
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("<Key>", 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("<Key>", 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("<<Opened>>")
|
||||||
|
self.hide = False
|
||||||
|
self.place_dropdown()
|
||||||
|
else:
|
||||||
|
self.place_forget()
|
||||||
|
self.hide = True
|
||||||
|
|
||||||
|
def _attach_key_press(self, k):
|
||||||
|
self.event_generate("<<Selected>>")
|
||||||
|
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)
|
||||||
158
MCE/student_list/EN.json
Normal file
158
MCE/student_list/EN.json
Normal file
@ -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)"
|
||||||
|
]
|
||||||
168
MCE/student_list/JP.json
Normal file
168
MCE/student_list/JP.json
Normal file
@ -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"
|
||||||
|
]
|
||||||
Loading…
Reference in New Issue
Block a user