Compare commits
28 Commits
dev
...
e7bba66083
| Author | SHA1 | Date | |
|---|---|---|---|
| e7bba66083 | |||
| 768da4d37f | |||
| 9216a36e0c | |||
| 50e9a1a9d9 | |||
| c26db5c179 | |||
| eb5d41914c | |||
| 668d77363a | |||
| 2cad7ebbac | |||
| bb76e0bba4 | |||
| 662b1535e9 | |||
| 85c6c5c127 | |||
| 2f3a357ff7 | |||
| 672bc30edb | |||
| 9126c58e9c | |||
| 42a9c964c9 | |||
| 6cf05da422 | |||
| 3680b22407 | |||
| 908b134e50 | |||
| ca2fc4ac46 | |||
| f1b6d4cd09 | |||
|
0d66a5c424
|
|||
|
37bbfba299
|
|||
|
34c5323df3
|
|||
|
30d63fa193
|
|||
|
be3fdb0988
|
|||
|
ba9472d0b1
|
|||
| 1791b4b05c | |||
|
f8d1ba3d4e
|
@@ -20,6 +20,7 @@ config/reloadalas
|
||||
test.py
|
||||
test/
|
||||
note.md
|
||||
MCE/config.json
|
||||
|
||||
# Created by .ignore support plugin (hsz.mobi)
|
||||
|
||||
|
||||
@@ -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()
|
||||
@@ -0,0 +1,437 @@
|
||||
import customtkinter
|
||||
import tkinter as tk
|
||||
import random
|
||||
import re
|
||||
from MCE.custom_widgets.ctkmessagebox import CTkMessagebox
|
||||
from MCE.custom_widgets.ctk_tooltip import CTkToolTip
|
||||
from MCE.custom_widgets.ctk_timeentry import CTkTimeEntry
|
||||
from MCE.custom_widgets.ctk_integerspinbox import CTkIntegerSpinbox
|
||||
from MCE.custom_widgets.ctk_templatedialog import CTkTemplateDialog
|
||||
from MCE.custom_widgets.ctk_notification import CTkNotification
|
||||
from MCE.utils import Linker, Config
|
||||
from filelock import FileLock, Timeout
|
||||
import threading
|
||||
import time
|
||||
|
||||
class MCE_Manager(customtkinter.CTk):
|
||||
def __init__(self, linker, config, **kwargs):
|
||||
super().__init__(**kwargs)
|
||||
self.linker = linker
|
||||
self.config = config
|
||||
self.create_widgets()
|
||||
# Load Template Data
|
||||
self.load_template_data()
|
||||
# Load queue Data
|
||||
self.load_queue_data()
|
||||
|
||||
def create_widgets(self):
|
||||
|
||||
self.create_mission_commissions_widgets()
|
||||
|
||||
|
||||
def create_mission_commissions_widgets(self):
|
||||
# Create Mission/Commissions/Event Checkbox
|
||||
self.create_mission_commissions_checkbox()
|
||||
|
||||
# Create Reset Daily Widgets
|
||||
self.create_reset_daily_widgets()
|
||||
|
||||
# Create Recharge AP and Event Checkboxes
|
||||
self.create_recharge_and_event_checkboxes()
|
||||
|
||||
# Create Preferred Template Selection
|
||||
self.create_preferred_template_selection()
|
||||
|
||||
# Create Mission Tabview with Template and Queue Tabs
|
||||
self.create_mission_tabview()
|
||||
|
||||
# Create Top-Level Window for Template Editing
|
||||
self.create_template_queue_editor()
|
||||
|
||||
# Create Template Frame and Queue Frame
|
||||
self.create_template_and_queue_frames()
|
||||
|
||||
# Create Lists to Store Frame Widgets
|
||||
self.create_frame_lists()
|
||||
|
||||
# Initialize Preferred Template and Templates List
|
||||
self.initialize_preferred_template()
|
||||
|
||||
# Create OptionMenu for Selecting a Template
|
||||
self.create_template_option_menu()
|
||||
|
||||
# Create Delete Template Button
|
||||
self.create_delete_template_button()
|
||||
|
||||
# Helper method to create Mission/Commissions/Event Checkbox
|
||||
def create_mission_commissions_checkbox(self):
|
||||
self.mission_commissions_checkbox = customtkinter.CTkLabel(self, text="Mission/Commissions/Event", width=60, font=customtkinter.CTkFont(family="Inter", size=20, weight="bold"))
|
||||
self.mission_commissions_checkbox.grid(row=11, column=0, sticky="nw", padx=20, pady=20)
|
||||
self.notification = CTkNotification(master=self, text="Config saved")
|
||||
self.notification.grid(row=11, column=1)
|
||||
|
||||
# Helper method to create Reset Daily Widgets
|
||||
def create_reset_daily_widgets(self):
|
||||
self.reset_daily = customtkinter.CTkCheckBox(self, text="Reset Daily", font=customtkinter.CTkFont(family="Inter", size=16, underline=True), command=lambda x=["ResetDaily"]: self.config.save_to_json(x))
|
||||
self.reset_daily.grid(row=12, column=0, sticky="nw", padx=80)
|
||||
self.reset_daily_tooltip = CTkToolTip(self.reset_daily, wraplength=400,
|
||||
message="If enabled and if current time >= reset time,\
|
||||
the queue will automatically be cleared and repopulated with preferred template stages. Only activated once a day.")
|
||||
|
||||
self.reset_daily_sub_label = customtkinter.CTkLabel(self, text="hh/mm/ss", font=customtkinter.CTkFont(family="Inter", size=12))
|
||||
self.reset_daily_sub_label.grid(row=13, column=0, padx=80)
|
||||
|
||||
self.reset_time = CTkTimeEntry(self)
|
||||
self.reset_time.grid(row=12, column=1)
|
||||
self.reset_time.hour_entry.bind("<KeyRelease>", lambda event, x=["ResetTime"]: self.config.save_to_json(x))
|
||||
self.reset_time.minute_entry.bind("<KeyRelease>", lambda event, x=["ResetTime"]: self.config.save_to_json(x))
|
||||
self.reset_time.second_entry.bind("<KeyRelease>", lambda event, x=["ResetTime"]: self.config.save_to_json(x))
|
||||
|
||||
self.linker.widgets["ResetDaily"] = self.reset_daily
|
||||
self.linker.widgets["ResetTime"] = self.reset_time
|
||||
|
||||
# Helper method to create Recharge AP and Event Checkboxes
|
||||
def create_recharge_and_event_checkboxes(self):
|
||||
self.recharge_checkbox = customtkinter.CTkCheckBox(self, text="Recharge AP", command=lambda x=["RechargeAP"]: self.config.save_to_json(x), font=customtkinter.CTkFont(family="Inter", size=16, underline=True))
|
||||
self.recharge_checkbox.grid(row=14, column=0, sticky="nw", padx=80, pady=20)
|
||||
self.linker.widgets["RechargeAP"] = self.recharge_checkbox
|
||||
self.recharge_tooltip = CTkToolTip(self.recharge_checkbox, wraplength=400,
|
||||
message="When enabled, recharge AP when low via cafe earnings, tasks, club and mailbox if they are enabled in their respective sections.")
|
||||
self.event_checkbox = customtkinter.CTkCheckBox(self, text="Sweep Event Stages", command=lambda x=["Event"]: self.config.save_to_json(x), font=customtkinter.CTkFont(family="Inter", size=16, underline=True))
|
||||
self.event_tooltip = CTkToolTip(self.event_checkbox, wraplength=400, message="When enabled, the script will sweep event stages. Otherwise, it will ignore them.")
|
||||
self.event_checkbox.grid(row=15, column=0, sticky="nw", padx=80)
|
||||
self.linker.widgets["Event"] = self.event_checkbox
|
||||
|
||||
# Helper method to create Preferred Template Selection
|
||||
def create_preferred_template_selection(self):
|
||||
self.templates = self.config.config_data["Templates"]
|
||||
self.templates_list = list(self.templates.keys())
|
||||
|
||||
self.preferred_template_label = customtkinter.CTkLabel(self, text="Preferred Template:", font=customtkinter.CTkFont(family="Inter", size=16, underline=True))
|
||||
self.preferred_template_label.grid(row=16, column=0, pady=20)
|
||||
self.preferred_template_tooltip = CTkToolTip(self.preferred_template_label, wraplength=400,
|
||||
message="The template from which to repopulate the queue when it is empty or reset daily is activated")
|
||||
self.preferred_template_optionmenu = customtkinter.CTkOptionMenu(self, values=self.templates_list, command=lambda x, y=["PreferredTemplate"]: self.config.save_to_json(y))
|
||||
self.preferred_template_optionmenu.grid(row=16, column=1, pady=20)
|
||||
self.linker.widgets["PreferredTemplate"] = self.preferred_template_optionmenu
|
||||
|
||||
# Helper method to create Mission Tabview with Template and Queue Tabs
|
||||
def create_mission_tabview(self):
|
||||
self.mission_tabview = customtkinter.CTkTabview(self, height=500)
|
||||
self.mission_tabview.grid(row=17, column=0, columnspan=3, padx=20)
|
||||
|
||||
self.tab_template = self.mission_tabview.add('Template')
|
||||
self.tab_queue = self.mission_tabview.add('Queue')
|
||||
|
||||
# Helper method to create Template Queue Editor
|
||||
def create_template_queue_editor(self):
|
||||
self.queue_buttons = []
|
||||
|
||||
for i in [self.tab_queue, self.tab_template]:
|
||||
queue = True if i == self.tab_queue else False
|
||||
|
||||
self.template_labels = customtkinter.CTkFrame(i)
|
||||
self.template_labels.grid(row=0, column=0, sticky="ew")
|
||||
|
||||
self.mode_label = customtkinter.CTkLabel(self.template_labels, text="Mode:", font=customtkinter.CTkFont(underline=True))
|
||||
self.mode_tooltip = CTkToolTip(self.mode_label, message="N : Mission Normal\nH : Mission Hard\nE : Event Quest\nXP : Commissions EXP\nCR : Commissions Credits\n", justify=tk.LEFT)
|
||||
self.mode_label.grid(row=1, column=0, padx=(130, 0), pady=5)
|
||||
|
||||
self.stage_label = customtkinter.CTkLabel(self.template_labels, text="Stage:", font=customtkinter.CTkFont(underline=True))
|
||||
self.stage_tooltip = CTkToolTip(self.stage_label, message="Valid format for Mission: 1-1\nValid format for Commissions/Event: 01")
|
||||
self.stage_label.grid(row=1, column=1, padx=(40, 20), pady=5)
|
||||
|
||||
self.run_times_label = customtkinter.CTkLabel(self.template_labels, text="Number of Sweeps:", font=customtkinter.CTkFont(underline=True))
|
||||
self.run_times_tooltip = CTkToolTip(self.run_times_label, message="How many times do you want to sweep the stage?")
|
||||
self.run_times_label.grid(row=1, column=2, pady=5)
|
||||
|
||||
self.template_buttons_frame = customtkinter.CTkFrame(i)
|
||||
self.template_buttons_frame.grid(row=3, column=0)
|
||||
|
||||
self.highlight_label = customtkinter.CTkLabel(self.template_buttons_frame, text="*You can double click an entry and press up or down arrow to change its position", font=customtkinter.CTkFont(family="Inter", size=12))
|
||||
self.highlight_label.grid(row=0, column=0, columnspan=3)
|
||||
|
||||
self.add_button = customtkinter.CTkButton(self.template_buttons_frame , text="Add", command=lambda queue=queue: self.add_frame(queue=queue))
|
||||
self.add_button.grid(row=1, column=0, padx=5, pady=5)
|
||||
|
||||
# Clear button to clear all frames
|
||||
self.clear_button = customtkinter.CTkButton(self.template_buttons_frame, text="Clear All", command=lambda queue=queue: self.clear_frames(queue=queue), fg_color="crimson")
|
||||
self.clear_button.grid(row=1, column=1, padx=5, pady=5)
|
||||
|
||||
# Save button to save data
|
||||
self.save_button = customtkinter.CTkButton(self.template_buttons_frame, text="Save", command=lambda queue=queue: self.save_data(queue=queue), fg_color="#DC621D")
|
||||
self.save_button.grid(row=1, column=2, padx=5, pady=5)
|
||||
if queue:
|
||||
self.queue_buttons = [self.add_button, self.clear_button, self.save_button]
|
||||
|
||||
# Helper method to create Template Frame and Queue Frame
|
||||
def create_template_and_queue_frames(self):
|
||||
self.template_frame = customtkinter.CTkScrollableFrame(self.tab_template, width=400, height=350)
|
||||
self.template_frame.grid(row=1, column=0, sticky="nsew")
|
||||
|
||||
self.queue_frame = customtkinter.CTkScrollableFrame(self.tab_queue, width=400, height=350)
|
||||
self.queue_frame.grid(row=1, column=0, sticky="nsew")
|
||||
|
||||
# Helper method to create Lists to Store Frame Widgets
|
||||
def create_frame_lists(self):
|
||||
self.template_frames = []
|
||||
self.queue_frames = []
|
||||
self.highlighted_frame = None
|
||||
|
||||
# Helper method to initialize Preferred Template and Templates List
|
||||
def initialize_preferred_template(self):
|
||||
self.preferred_template = self.config.config_data["PreferredTemplate"]
|
||||
self.templates_list.append("Add New Template")
|
||||
|
||||
# Helper method to create OptionMenu for Selecting a Template
|
||||
def create_template_option_menu(self):
|
||||
self.selected_template = tk.StringVar(self.template_frame)
|
||||
self.selected_template.set(self.preferred_template) # Set the initial value to the preferred template
|
||||
|
||||
self.template_optionmenu = customtkinter.CTkOptionMenu(self.template_labels, values=self.templates_list, variable=self.selected_template, command=lambda *args: self.load_template_data())
|
||||
self.template_optionmenu.grid(row=0, column=0, padx=5, pady=5)
|
||||
|
||||
# Helper method to create Delete Template Button
|
||||
def create_delete_template_button(self):
|
||||
self.delete_template_button = customtkinter.CTkButton(self.template_labels, width=40, text="Delete", command=self.delete_template)
|
||||
self.delete_template_button.grid(row=0, column=1)
|
||||
|
||||
# Helper method to add frames from Configuration Data
|
||||
def load_queue_data(self, state="normal"):
|
||||
for entry in self.config.config_data['Queue']:
|
||||
self.add_frame(entry, queue=True, state=state)
|
||||
|
||||
# Function to load template data into frames
|
||||
def load_template_data(self):
|
||||
selected = self.selected_template.get()
|
||||
if selected == "Add New Template":
|
||||
dialog = CTkTemplateDialog(text="Type in new template name. Template name MUST be different from other templates!", title="Template Name", values=self.templates_list[:-1])
|
||||
template_name, template_import = dialog.get_input()
|
||||
if template_name.replace(" ", "") == "":
|
||||
self.template_optionmenu.set(self.previous_selected)
|
||||
return
|
||||
elif template_name in self.templates_list:
|
||||
CTkMessagebox(title="Error", message="Name is invalid.", icon="cancel")
|
||||
self.template_optionmenu.set(self.previous_selected)
|
||||
return
|
||||
else:
|
||||
if template_import != "":
|
||||
self.templates[template_name] = self.templates[template_import]
|
||||
else:
|
||||
self.templates[template_name] = []
|
||||
self.templates_list[-1] = template_name
|
||||
self.preferred_template_optionmenu.configure(values=self.templates_list)
|
||||
selected = template_name
|
||||
self.template_optionmenu.set(selected)
|
||||
self.templates_list.append("Add New Template")
|
||||
self.template_optionmenu.configure(values=self.templates_list)
|
||||
self.clear_frames()
|
||||
for entry in self.templates[selected]:
|
||||
self.add_frame(entry)
|
||||
self.previous_selected = selected
|
||||
|
||||
def delete_template(self):
|
||||
msg = CTkMessagebox(title="Template Deletetion", message=f"Are you sure you want to delete Template {self.previous_selected}?",
|
||||
icon="question", option_1="No", option_2="Yes")
|
||||
response = msg.get()
|
||||
if response=="Yes":
|
||||
if len(self.templates) != 1:
|
||||
del self.templates[self.previous_selected]
|
||||
self.templates_list = list(self.templates.keys())
|
||||
self.preferred_template_optionmenu.configure(values=self.templates_list)
|
||||
if self.preferred_template == self.previous_selected:
|
||||
self.preferred_template = random.choice(self.templates_list)
|
||||
self.config.config_data["PreferredTemplate"] = self.preferred_template
|
||||
self.selected_template.set(self.preferred_template) # Set the initial value to the preferred template
|
||||
self.preferred_template_optionmenu.set(self.preferred_template)
|
||||
self.load_template_data()
|
||||
self.config.save_file()
|
||||
self.templates_list.append("Add New Template")
|
||||
self.template_optionmenu.configure(values=self.templates_list)
|
||||
self.template_optionmenu.set(self.preferred_template)
|
||||
else:
|
||||
CTkMessagebox(title="Error", message="At least one template must exist!!!", icon="cancel")
|
||||
return
|
||||
|
||||
# Function to add a frame with widgets
|
||||
def add_frame(self, inner_list=None, queue=False, state="normal"):
|
||||
frames = self.queue_frames if queue else self.template_frames
|
||||
parent_frame = self.queue_frame if queue else self.template_frame
|
||||
row_index = len(frames) + 1 # Calculate the row for the new frame
|
||||
# Create a frame
|
||||
frame = tk.Frame(parent_frame, bg="gray17")
|
||||
frame.grid(row=row_index, column=0, columnspan=4, padx=10, pady=10, sticky="w")
|
||||
frames.append(frame)
|
||||
# "Up" button to move the frame up
|
||||
up_button = customtkinter.CTkButton(frame, text="Up", width=5, command=lambda f=frame, queue=queue: self.move_frame_up(f, queue), state=state)
|
||||
up_button.grid(row=0, column=0, padx=5, pady=5, sticky="w")
|
||||
# "Down" button to move the frame down
|
||||
down_button = customtkinter.CTkButton(frame, text="Down", width=5, command=lambda f=frame, queue=queue: self.move_frame_down(f, queue), state=state)
|
||||
down_button.grid(row=0, column=1, padx=5, pady=5, sticky="w")
|
||||
# Dropdown menu for mode
|
||||
mode_optionmenu = customtkinter.CTkOptionMenu(frame, width=60, values=["N", "H", "E", "XP", "CR"], state=state)
|
||||
mode_optionmenu.set(inner_list[0] if inner_list else "N")
|
||||
mode_optionmenu.grid(row=0, column=2, padx=5, pady=5, sticky="w")
|
||||
# Entry widget for stage
|
||||
stage_var = tk.StringVar(value=inner_list[1] if inner_list else "")
|
||||
stage_entry = customtkinter.CTkEntry(frame, width=60, textvariable=stage_var, state=state)
|
||||
stage_entry.grid(row=0, column=3, padx=5, pady=5, sticky="w")
|
||||
mode_optionmenu.configure(command=lambda choice, x=mode_optionmenu, y=stage_entry : self.check_entry(x,y))
|
||||
stage_entry.bind('<KeyRelease>', command=lambda event, x=mode_optionmenu, y=stage_entry : self.check_entry(x,y))
|
||||
self.check_entry(mode_optionmenu, stage_entry)
|
||||
# Entry widget for run times (only accepts numbers)
|
||||
run_times_spinbox = CTkIntegerSpinbox(frame, step_size=1, min_value=1)
|
||||
run_times_spinbox.set(value=inner_list[2] if inner_list else 1)
|
||||
run_times_spinbox.grid(row=0, column=4, padx=5, pady=5, sticky="w")
|
||||
|
||||
# Delete button to delete the frame
|
||||
delete_button = customtkinter.CTkButton(frame, text="Delete", width=5, command=lambda f=frame, queue=queue: self.delete_frame(f, queue), state=state)
|
||||
delete_button.grid(row=0, column=5, padx=5, pady=5, sticky="w")
|
||||
|
||||
frame.bind("<Double-Button-1>", lambda event, f=frame: self.highlight_frame(f))
|
||||
|
||||
# Function to clear all frames
|
||||
def clear_frames(self, queue=False):
|
||||
frames = self.queue_frames if queue else self.template_frames
|
||||
for frame in frames:
|
||||
frame.destroy()
|
||||
frames.clear()
|
||||
|
||||
# Function to save frames as data
|
||||
def save_data(self, queue=False):
|
||||
entries = []
|
||||
frames = self.queue_frames if queue else self.template_frames
|
||||
name = "Queue" if queue else "Template"
|
||||
for frame in frames:
|
||||
mode_optionmenu = frame.winfo_children()[2]
|
||||
stage_entry = frame.winfo_children()[3]
|
||||
if not self.check_entry(mode_optionmenu, stage_entry):
|
||||
CTkMessagebox(title="Error", message="Configuration not saved. Some entries are incomplete or have incorect input.", icon="cancel")
|
||||
return
|
||||
mode = frame.winfo_children()[2].get()
|
||||
stage = frame.winfo_children()[3].get().strip()
|
||||
run_times = frame.winfo_children()[4].get()
|
||||
entries.append([mode, stage, int(run_times)])
|
||||
if queue:
|
||||
self.config.config_data['Queue'] = entries
|
||||
else:
|
||||
selected = self.selected_template.get()
|
||||
self.templates[selected] = entries
|
||||
self.config.save_file(name)
|
||||
|
||||
def check_entry(self, mode_dropdown, stage_entry):
|
||||
mode = mode_dropdown.get()
|
||||
stage = stage_entry.get()
|
||||
if mode in ["N", "H"]:
|
||||
pattern = r'\d{1,2}-[0-9A-Z]'
|
||||
else:
|
||||
pattern = r"^\d{2}$"
|
||||
if re.match(pattern, stage):
|
||||
stage_entry.configure(border_color=['#979DA2', '#565B5E'])
|
||||
return True
|
||||
else:
|
||||
stage_entry.configure(border_color='crimson')
|
||||
return False
|
||||
|
||||
# Function to move a frame up
|
||||
def move_frame_up(self, frame, queue=False):
|
||||
frames = self.queue_frames if queue else self.template_frames
|
||||
index = frames.index(frame)
|
||||
if index > 0:
|
||||
frames[index], frames[index - 1] = frames[index - 1], frames[index]
|
||||
self.update_frame_positions(queue=queue)
|
||||
|
||||
# Function to move a frame down
|
||||
def move_frame_down(self, frame, queue=False):
|
||||
frames = self.queue_frames if queue else self.template_frames
|
||||
index = frames.index(frame)
|
||||
if index < len(frames) - 1:
|
||||
frames[index], frames[index + 1] = frames[index + 1], frames[index]
|
||||
self.update_frame_positions(queue=queue)
|
||||
|
||||
# Function to update frame positions in the grid
|
||||
def update_frame_positions(self, queue=False):
|
||||
frames = self.queue_frames if queue else self.template_frames
|
||||
for index, frame in enumerate(frames):
|
||||
frame.grid(row=index, column=0, columnspan=4, padx=10, pady=10, sticky="w")
|
||||
|
||||
# Function to delete a frame
|
||||
def delete_frame(self, frame, queue=False):
|
||||
if queue:
|
||||
self.queue_frames.remove(frame)
|
||||
else:
|
||||
self.template_frames.remove(frame)
|
||||
frame.destroy()
|
||||
# Update the positions of remaining frames
|
||||
self.update_frame_positions(queue=queue)
|
||||
|
||||
def highlight_frame(self, frame):
|
||||
try:
|
||||
if self.highlighted_frame is not None:
|
||||
self.highlighted_frame.unbind("<Up>")
|
||||
self.highlighted_frame.unbind("<Down>")
|
||||
self.highlighted_frame.config(bg="gray17")
|
||||
except:
|
||||
pass
|
||||
|
||||
if self.highlighted_frame == frame:
|
||||
self.highlighted_frame = None
|
||||
|
||||
else:
|
||||
up_button = frame.winfo_children()[0]
|
||||
down_button = frame.winfo_children()[1]
|
||||
frame.config(bg="yellow")
|
||||
frame.bind("<Up>", lambda event: up_button.invoke())
|
||||
frame.bind("<Down>", lambda event: down_button.invoke())
|
||||
frame.focus_set()
|
||||
self.highlighted_frame = frame
|
||||
|
||||
def check_lock(self):
|
||||
while 1:
|
||||
try:
|
||||
lock = FileLock("MCE\config.json.lock")
|
||||
lock.acquire(timeout=1)
|
||||
except Timeout:
|
||||
if not self.config.locked:
|
||||
self.after(10, lambda : (self.queue_changed(), self.update_queue(), self.switch_queue_state("disabled")))
|
||||
self.config.locked = True
|
||||
elif self.config.locked and self.queue_changed():
|
||||
self.after(10, lambda : (self.update_queue(), self.switch_queue_state("disabled")))
|
||||
else:
|
||||
lock.release()
|
||||
if self.config.locked:
|
||||
self.after(10, lambda : (self.queue_changed(), self.update_queue(), self.switch_queue_state("normal")))
|
||||
self.config.locked = False
|
||||
finally:
|
||||
time.sleep(2)
|
||||
|
||||
def switch_queue_state(self, state):
|
||||
for button in self.queue_buttons:
|
||||
button.configure(state=state)
|
||||
for frame in self.queue_frames:
|
||||
for widget in frame.winfo_children():
|
||||
widget.configure(state=state)
|
||||
|
||||
def update_queue(self):
|
||||
self.clear_frames(queue=True)
|
||||
for entry in self.config.config_data['Queue']:
|
||||
self.add_frame(entry, queue=True)
|
||||
|
||||
def queue_changed(self):
|
||||
new_config_data = self.config.read()
|
||||
changed = self.config.config_data["Queue"] != new_config_data["Queue"] or self.config.config_data["LastRun"] != new_config_data["LastRun"]
|
||||
if changed:
|
||||
self.config.config_data["LastRun"] = new_config_data["LastRun"]
|
||||
self.config.config_data['Queue'] = new_config_data['Queue']
|
||||
return changed
|
||||
|
||||
if __name__ == "__main__":
|
||||
linker = Linker()
|
||||
config = Config(linker, "MCE\config.json")
|
||||
app = MCE_Manager(linker, config)
|
||||
app.title("MCE Manager")
|
||||
linker.sidebar = app
|
||||
config.load_config()
|
||||
daemon_thread = threading.Thread(target=app.check_lock, daemon=True)
|
||||
daemon_thread.start()
|
||||
app.mainloop()
|
||||
@@ -0,0 +1,95 @@
|
||||
import customtkinter
|
||||
import re
|
||||
from typing import Callable
|
||||
|
||||
class CTkIntegerSpinbox(customtkinter.CTkFrame):
|
||||
def __init__(self, *args,
|
||||
width: int = 100,
|
||||
height: int = 32,
|
||||
step_size: int = 1,
|
||||
min_value: int = 0,
|
||||
command: Callable = None,
|
||||
**kwargs):
|
||||
super().__init__(*args, width=width, height=height, **kwargs)
|
||||
|
||||
self.step_size = step_size
|
||||
self.min_value = min_value
|
||||
self.command = command
|
||||
self.after_id = None
|
||||
|
||||
self.grid_columnconfigure((0, 2), weight=0)
|
||||
self.grid_columnconfigure(1, weight=1)
|
||||
|
||||
self.subtract_button = customtkinter.CTkButton(self, text="-", width=height-6, height=height-6)
|
||||
self.subtract_button.grid(row=0, column=0, padx=(3, 0), pady=3)
|
||||
self.subtract_button.bind('<ButtonPress-1>', self.start_decrementing)
|
||||
self.subtract_button.bind('<ButtonRelease-1>', self.stop_decrementing)
|
||||
|
||||
self.entry = customtkinter.CTkEntry(self, width=width-(2*height), height=height-6, border_width=0)
|
||||
self.entry.grid(row=0, column=1, columnspan=1, padx=3, pady=3, sticky="ew")
|
||||
|
||||
self.add_button = customtkinter.CTkButton(self, text="+", width=height-6, height=height-6)
|
||||
self.add_button.grid(row=0, column=2, padx=(0, 3), pady=3)
|
||||
self.add_button.bind('<ButtonPress-1>', self.start_incrementing)
|
||||
self.add_button.bind('<ButtonRelease-1>', self.stop_incrementing)
|
||||
|
||||
self.entry.insert(0, "0")
|
||||
|
||||
# Configure validatecommand to allow only integers
|
||||
vcmd = (self.entry.register(self.validate_input), '%P')
|
||||
self.entry.configure(validate='key', validatecommand=vcmd)
|
||||
|
||||
def start_incrementing(self, event):
|
||||
self.increment()
|
||||
self.after_id = self.after(150, self.start_incrementing, event)
|
||||
|
||||
def stop_incrementing(self, event):
|
||||
if self.after_id:
|
||||
self.after_cancel(self.after_id)
|
||||
self.after_id = None
|
||||
|
||||
def start_decrementing(self, event):
|
||||
self.decrement()
|
||||
self.after_id = self.after(150, self.start_decrementing, event)
|
||||
|
||||
def stop_decrementing(self, event):
|
||||
if self.after_id:
|
||||
self.after_cancel(self.after_id)
|
||||
self.after_id = None
|
||||
|
||||
def increment(self):
|
||||
value = int(self.entry.get()) + self.step_size
|
||||
self.entry.delete(0, "end")
|
||||
self.entry.insert(0, max(self.min_value, value)) # Ensure the value is not less than 1
|
||||
if self.command is not None:
|
||||
self.command()
|
||||
|
||||
def decrement(self):
|
||||
value = int(self.entry.get()) - self.step_size
|
||||
self.entry.delete(0, "end")
|
||||
self.entry.insert(0, max(self.min_value, value)) # Ensure the value is not less than 0
|
||||
if self.command is not None:
|
||||
self.command()
|
||||
|
||||
def validate_input(self, new_value):
|
||||
# Validate that the input is a non-negative integer
|
||||
return re.match(r'^\d*$', new_value) is not None
|
||||
|
||||
def get(self) -> int:
|
||||
try:
|
||||
return int(self.entry.get())
|
||||
except ValueError:
|
||||
return 0
|
||||
|
||||
def set(self, value: int):
|
||||
self.entry.delete(0, "end")
|
||||
self.entry.insert(0, max(self.min_value, value)) # Ensure the value is not less than 0
|
||||
|
||||
def configure(self, **kwargs):
|
||||
state = kwargs.get("state", None)
|
||||
if state is not None:
|
||||
self.subtract_button.configure(state=state)
|
||||
self.add_button.configure(state=state)
|
||||
self.entry.configure(state=state)
|
||||
kwargs.pop("state")
|
||||
super().configure(**kwargs)
|
||||
@@ -0,0 +1,27 @@
|
||||
import customtkinter
|
||||
class CTkNotification(customtkinter.CTkFrame):
|
||||
def __init__(self, text, master, **kwargs):
|
||||
self.master_color = master.cget("fg_color")
|
||||
super().__init__(master=master, **kwargs, fg_color=self.master_color)
|
||||
self.label = customtkinter.CTkLabel(self, text=text, text_color=self.master_color, width=200, wraplength=200, font=("Inter", 16))
|
||||
self.label.grid(row=0, column=0, sticky="nsew")
|
||||
self.close_button = customtkinter.CTkButton(
|
||||
self, width=40, text="X", text_color_disabled=self.master_color, command=self.hide, fg_color="transparent", state="disabled")
|
||||
self.close_button.grid(row=0, column=1)
|
||||
self.progress_bar = customtkinter.CTkProgressBar(self, determinate_speed=0.4, fg_color=self.master_color, progress_color=self.master_color)
|
||||
self.progress_bar.grid(row=1, column=0, columnspan=2, sticky="nsew")
|
||||
|
||||
def hide(self):
|
||||
self.configure(fg_color="transparent")
|
||||
self.progress_bar.stop()
|
||||
self.progress_bar.configure(progress_color=self.master_color)
|
||||
self.close_button.configure(state="disabled")
|
||||
self.label.configure(text_color=self.master_color)
|
||||
|
||||
def show(self):
|
||||
self.configure(fg_color="green")
|
||||
self.progress_bar.configure(progress_color="white")
|
||||
self.progress_bar.set(0)
|
||||
self.progress_bar.start()
|
||||
self.close_button.configure(state="normal")
|
||||
self.label.configure(text_color="white")
|
||||
@@ -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)
|
||||
@@ -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)
|
||||
@@ -0,0 +1,133 @@
|
||||
from typing import Union, Tuple, Optional
|
||||
from customtkinter import CTkLabel, CTkEntry, CTkButton, ThemeManager, CTkToplevel, CTkFont, CTkOptionMenu
|
||||
|
||||
|
||||
class CTkTemplateDialog(CTkToplevel):
|
||||
"""
|
||||
Dialog with extra window, message, entry widget, cancel and ok button.
|
||||
For detailed information check out the documentation.
|
||||
"""
|
||||
|
||||
def __init__(self,
|
||||
fg_color: Optional[Union[str, Tuple[str, str]]] = None,
|
||||
text_color: Optional[Union[str, Tuple[str, str]]] = None,
|
||||
button_fg_color: Optional[Union[str, Tuple[str, str]]] = None,
|
||||
button_hover_color: Optional[Union[str, Tuple[str, str]]] = None,
|
||||
button_text_color: Optional[Union[str, Tuple[str, str]]] = None,
|
||||
entry_fg_color: Optional[Union[str, Tuple[str, str]]] = None,
|
||||
entry_border_color: Optional[Union[str, Tuple[str, str]]] = None,
|
||||
entry_text_color: Optional[Union[str, Tuple[str, str]]] = None,
|
||||
|
||||
title: str = "CTkDialog",
|
||||
font: Optional[Union[tuple, CTkFont]] = None,
|
||||
text: str = "CTkDialog",
|
||||
values = []):
|
||||
|
||||
super().__init__(fg_color=fg_color)
|
||||
|
||||
self._fg_color = ThemeManager.theme["CTkToplevel"]["fg_color"] if fg_color is None else self._check_color_type(fg_color)
|
||||
self._text_color = ThemeManager.theme["CTkLabel"]["text_color"] if text_color is None else self._check_color_type(button_hover_color)
|
||||
self._button_fg_color = ThemeManager.theme["CTkButton"]["fg_color"] if button_fg_color is None else self._check_color_type(button_fg_color)
|
||||
self._button_hover_color = ThemeManager.theme["CTkButton"]["hover_color"] if button_hover_color is None else self._check_color_type(button_hover_color)
|
||||
self._button_text_color = ThemeManager.theme["CTkButton"]["text_color"] if button_text_color is None else self._check_color_type(button_text_color)
|
||||
self._entry_fg_color = ThemeManager.theme["CTkEntry"]["fg_color"] if entry_fg_color is None else self._check_color_type(entry_fg_color)
|
||||
self._entry_border_color = ThemeManager.theme["CTkEntry"]["border_color"] if entry_border_color is None else self._check_color_type(entry_border_color)
|
||||
self._entry_text_color = ThemeManager.theme["CTkEntry"]["text_color"] if entry_text_color is None else self._check_color_type(entry_text_color)
|
||||
|
||||
self._user_input = ("", "")
|
||||
self._running: bool = False
|
||||
self._title = title
|
||||
self._text = text
|
||||
self._font = font
|
||||
self._values = [""] + values
|
||||
|
||||
self.title(self._title)
|
||||
self.lift() # lift window on top
|
||||
self.attributes("-topmost", True) # stay on top
|
||||
self.protocol("WM_DELETE_WINDOW", self._on_closing)
|
||||
self.after(10, self._create_widgets) # create widgets with slight delay, to avoid white flickering of background
|
||||
self.resizable(False, False)
|
||||
self.grab_set() # make other windows not clickable
|
||||
|
||||
def _create_widgets(self):
|
||||
self.grid_columnconfigure((0, 1), weight=1)
|
||||
self.rowconfigure(0, weight=1)
|
||||
|
||||
self._label = CTkLabel(master=self,
|
||||
width=300,
|
||||
wraplength=300,
|
||||
fg_color="transparent",
|
||||
text_color=self._text_color,
|
||||
text=self._text,
|
||||
font=self._font)
|
||||
self._label.grid(row=0, column=0, columnspan=2, padx=20, pady=20, sticky="ew")
|
||||
|
||||
self._entry = CTkEntry(master=self,
|
||||
width=230,
|
||||
fg_color=self._entry_fg_color,
|
||||
border_color=self._entry_border_color,
|
||||
text_color=self._entry_text_color,
|
||||
font=self._font)
|
||||
self._entry.grid(row=1, column=0, columnspan=2, padx=20, pady=(0, 20), sticky="ew")
|
||||
|
||||
self._label2 = CTkLabel(master=self,
|
||||
width=100,
|
||||
wraplength=100,
|
||||
fg_color="transparent",
|
||||
text_color=self._text_color,
|
||||
text="Import stages from: ",
|
||||
font=self._font)
|
||||
self._label2.grid(row=2, column=0, columnspan=1, padx=(20, 10), pady=(0, 20), sticky="ew")
|
||||
|
||||
self._template_optionmenu = CTkOptionMenu(master=self,
|
||||
width=100,
|
||||
fg_color=self._button_fg_color,
|
||||
button_hover_color=self._button_hover_color,
|
||||
text_color=self._button_text_color,
|
||||
font=self._font,
|
||||
values=self._values
|
||||
)
|
||||
self._template_optionmenu.grid(row=2, column=1, columnspan=1, padx=(10, 20), pady=(0, 20), sticky="ew")
|
||||
|
||||
|
||||
self._ok_button = CTkButton(master=self,
|
||||
width=100,
|
||||
border_width=0,
|
||||
fg_color=self._button_fg_color,
|
||||
hover_color=self._button_hover_color,
|
||||
text_color=self._button_text_color,
|
||||
text='Ok',
|
||||
font=self._font,
|
||||
command=self._ok_event)
|
||||
self._ok_button.grid(row=3, column=0, columnspan=1, padx=(20, 10), pady=(0, 20), sticky="ew")
|
||||
|
||||
self._cancel_button = CTkButton(master=self,
|
||||
width=100,
|
||||
border_width=0,
|
||||
fg_color=self._button_fg_color,
|
||||
hover_color=self._button_hover_color,
|
||||
text_color=self._button_text_color,
|
||||
text='Cancel',
|
||||
font=self._font,
|
||||
command=self._cancel_event)
|
||||
self._cancel_button.grid(row=3, column=1, columnspan=1, padx=(10, 20), pady=(0, 20), sticky="ew")
|
||||
|
||||
self.after(150, lambda: self._entry.focus()) # set focus to entry with slight delay, otherwise it won't work
|
||||
self._entry.bind("<Return>", self._ok_event)
|
||||
|
||||
def _ok_event(self, event=None):
|
||||
self._user_input = self._entry.get(), self._template_optionmenu.get()
|
||||
self.grab_release()
|
||||
self.destroy()
|
||||
|
||||
def _on_closing(self):
|
||||
self.grab_release()
|
||||
self.destroy()
|
||||
|
||||
def _cancel_event(self):
|
||||
self.grab_release()
|
||||
self.destroy()
|
||||
|
||||
def get_input(self):
|
||||
self.master.wait_window(self)
|
||||
return self._user_input
|
||||
@@ -0,0 +1,36 @@
|
||||
import customtkinter
|
||||
import tkinter as tk
|
||||
|
||||
class CTkTimeEntry(customtkinter.CTkFrame):
|
||||
def __init__(self, master=None, **kwargs):
|
||||
super().__init__(master, **kwargs)
|
||||
self.hour = tk.StringVar()
|
||||
self.minute = tk.StringVar()
|
||||
self.second = tk.StringVar()
|
||||
|
||||
self.hour_entry = customtkinter.CTkEntry(self, width=50, textvariable=self.hour, validate="key", validatecommand=(self.register(self.validate_hour), '%P'))
|
||||
self.hour_entry.pack(side=tk.LEFT)
|
||||
|
||||
self.minute_entry = customtkinter.CTkEntry(self,width=50, textvariable=self.minute, validate="key", validatecommand=(self.register(self.validate_min_sec), '%P'))
|
||||
self.minute_entry.pack(side=tk.LEFT)
|
||||
|
||||
self.second_entry = customtkinter.CTkEntry(self, width=50, textvariable=self.second, validate="key", validatecommand=(self.register(self.validate_min_sec), '%P'))
|
||||
self.second_entry.pack(side=tk.LEFT)
|
||||
|
||||
def validate_hour(self, P):
|
||||
return len(P) <= 2 and (P.isdigit() and int(P) <= 23 or P == "")
|
||||
|
||||
def validate_min_sec(self, P):
|
||||
return len(P) <= 2 and (P.isdigit() and int(P) <= 59 or P == "")
|
||||
|
||||
def set(self, time_str):
|
||||
h, m, s = map(str, time_str.split(':'))
|
||||
self.hour.set(h)
|
||||
self.minute.set(m)
|
||||
self.second.set(s)
|
||||
|
||||
def get(self):
|
||||
h = self.hour.get() if self.hour.get() else "00"
|
||||
m = self.minute.get() if self.minute.get() else "00"
|
||||
s = self.second.get() if self.second.get() else "00"
|
||||
return f"{h.zfill(2)}:{m.zfill(2)}:{s.zfill(2)}"
|
||||
@@ -0,0 +1,212 @@
|
||||
"""
|
||||
CTkToolTip Widget
|
||||
version: 0.8
|
||||
"""
|
||||
|
||||
import time
|
||||
import sys
|
||||
import customtkinter
|
||||
from tkinter import Toplevel, Frame
|
||||
|
||||
|
||||
class CTkToolTip(Toplevel):
|
||||
"""
|
||||
Creates a ToolTip (pop-up) widget for customtkinter.
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
widget: any = None,
|
||||
message: str = None,
|
||||
delay: float = 0.2,
|
||||
follow: bool = True,
|
||||
x_offset: int = +20,
|
||||
y_offset: int = +10,
|
||||
bg_color: str = None,
|
||||
corner_radius: int = 10,
|
||||
border_width: int = 0,
|
||||
border_color: str = None,
|
||||
alpha: float = 0.95,
|
||||
padding: tuple = (10, 2),
|
||||
**message_kwargs):
|
||||
|
||||
super().__init__()
|
||||
|
||||
self.widget = widget
|
||||
|
||||
self.withdraw()
|
||||
|
||||
# Disable ToolTip's title bar
|
||||
self.overrideredirect(True)
|
||||
|
||||
if sys.platform.startswith("win"):
|
||||
self.transparent_color = self.widget._apply_appearance_mode(
|
||||
customtkinter.ThemeManager.theme["CTkToplevel"]["fg_color"])
|
||||
self.attributes("-transparentcolor", self.transparent_color)
|
||||
self.transient()
|
||||
elif sys.platform.startswith("darwin"):
|
||||
self.transparent_color = 'systemTransparent'
|
||||
self.attributes("-transparent", True)
|
||||
self.transient(self.master)
|
||||
else:
|
||||
self.transparent_color = '#000001'
|
||||
corner_radius = 0
|
||||
self.transient()
|
||||
|
||||
self.resizable(width=True, height=True)
|
||||
|
||||
# Make the background transparent
|
||||
self.config(background=self.transparent_color)
|
||||
|
||||
# StringVar instance for msg string
|
||||
self.messageVar = customtkinter.StringVar()
|
||||
self.message = message
|
||||
self.messageVar.set(self.message)
|
||||
|
||||
self.delay = delay
|
||||
self.follow = follow
|
||||
self.x_offset = x_offset
|
||||
self.y_offset = y_offset
|
||||
self.corner_radius = corner_radius
|
||||
self.alpha = alpha
|
||||
self.border_width = border_width
|
||||
self.padding = padding
|
||||
self.bg_color = customtkinter.ThemeManager.theme["CTkFrame"]["fg_color"] if bg_color is None else bg_color
|
||||
self.border_color = border_color
|
||||
self.disable = False
|
||||
|
||||
# visibility status of the ToolTip inside|outside|visible
|
||||
self.status = "outside"
|
||||
self.last_moved = 0
|
||||
self.attributes('-alpha', self.alpha)
|
||||
|
||||
if sys.platform.startswith("win"):
|
||||
if self.widget._apply_appearance_mode(self.bg_color) == self.transparent_color:
|
||||
self.transparent_color = "#000001"
|
||||
self.config(background=self.transparent_color)
|
||||
self.attributes("-transparentcolor", self.transparent_color)
|
||||
|
||||
# Add the message widget inside the tooltip
|
||||
self.transparent_frame = Frame(self, bg=self.transparent_color)
|
||||
self.transparent_frame.pack(padx=0, pady=0, fill="both", expand=True)
|
||||
|
||||
self.frame = customtkinter.CTkFrame(self.transparent_frame, bg_color=self.transparent_color,
|
||||
corner_radius=self.corner_radius,
|
||||
border_width=self.border_width, fg_color=self.bg_color,
|
||||
border_color=self.border_color)
|
||||
self.frame.pack(padx=0, pady=0, fill="both", expand=True)
|
||||
|
||||
self.message_label = customtkinter.CTkLabel(self.frame, textvariable=self.messageVar, **message_kwargs)
|
||||
self.message_label.pack(fill="both", padx=self.padding[0] + self.border_width,
|
||||
pady=self.padding[1] + self.border_width, expand=True)
|
||||
|
||||
if self.widget.winfo_name() != "tk":
|
||||
if self.frame.cget("fg_color") == self.widget.cget("bg_color"):
|
||||
if not bg_color:
|
||||
self._top_fg_color = self.frame._apply_appearance_mode(
|
||||
customtkinter.ThemeManager.theme["CTkFrame"]["top_fg_color"])
|
||||
if self._top_fg_color != self.transparent_color:
|
||||
self.frame.configure(fg_color=self._top_fg_color)
|
||||
|
||||
# Add bindings to the widget without overriding the existing ones
|
||||
self.widget.bind("<Enter>", self.on_enter, add="+")
|
||||
self.widget.bind("<Leave>", self.on_leave, add="+")
|
||||
self.widget.bind("<Motion>", self.on_enter, add="+")
|
||||
self.widget.bind("<B1-Motion>", self.on_enter, add="+")
|
||||
self.widget.bind("<Destroy>", lambda _: self.hide(), add="+")
|
||||
|
||||
def show(self) -> None:
|
||||
"""
|
||||
Enable the widget.
|
||||
"""
|
||||
self.disable = False
|
||||
|
||||
def on_enter(self, event) -> None:
|
||||
"""
|
||||
Processes motion within the widget including entering and moving.
|
||||
"""
|
||||
|
||||
if self.disable:
|
||||
return
|
||||
self.last_moved = time.time()
|
||||
|
||||
# Set the status as inside for the very first time
|
||||
if self.status == "outside":
|
||||
self.status = "inside"
|
||||
|
||||
# If the follow flag is not set, motion within the widget will make the ToolTip dissapear
|
||||
if not self.follow:
|
||||
self.status = "inside"
|
||||
self.withdraw()
|
||||
|
||||
# Calculate available space on the right side of the widget relative to the screen
|
||||
root_width = self.winfo_screenwidth()
|
||||
widget_x = event.x_root
|
||||
space_on_right = root_width - widget_x
|
||||
|
||||
# Calculate the width of the tooltip's text based on the length of the message string
|
||||
text_width = self.message_label.winfo_reqwidth()
|
||||
|
||||
# Calculate the offset based on available space and text width to avoid going off-screen on the right side
|
||||
offset_x = self.x_offset
|
||||
if space_on_right < text_width + 20: # Adjust the threshold as needed
|
||||
offset_x = -text_width - 20 # Negative offset when space is limited on the right side
|
||||
|
||||
# Offsets the ToolTip using the coordinates od an event as an origin
|
||||
self.geometry(f"+{event.x_root + offset_x}+{event.y_root + self.y_offset}")
|
||||
|
||||
# Time is in integer: milliseconds
|
||||
self.after(int(self.delay * 1000), self._show)
|
||||
|
||||
def on_leave(self, event=None) -> None:
|
||||
"""
|
||||
Hides the ToolTip temporarily.
|
||||
"""
|
||||
|
||||
if self.disable: return
|
||||
self.status = "outside"
|
||||
self.withdraw()
|
||||
|
||||
def _show(self) -> None:
|
||||
"""
|
||||
Displays the ToolTip.
|
||||
"""
|
||||
|
||||
if not self.widget.winfo_exists():
|
||||
self.hide()
|
||||
self.destroy()
|
||||
|
||||
if self.status == "inside" and time.time() - self.last_moved >= self.delay:
|
||||
self.status = "visible"
|
||||
self.deiconify()
|
||||
|
||||
def hide(self) -> None:
|
||||
"""
|
||||
Disable the widget from appearing.
|
||||
"""
|
||||
if not self.winfo_exists():
|
||||
return
|
||||
self.withdraw()
|
||||
self.disable = True
|
||||
|
||||
def is_disabled(self) -> None:
|
||||
"""
|
||||
Return the window state
|
||||
"""
|
||||
return self.disable
|
||||
|
||||
def get(self) -> None:
|
||||
"""
|
||||
Returns the text on the tooltip.
|
||||
"""
|
||||
return self.messageVar.get()
|
||||
|
||||
def configure(self, message: str = None, delay: float = None, bg_color: str = None, **kwargs):
|
||||
"""
|
||||
Set new message or configure the label parameters.
|
||||
"""
|
||||
if delay: self.delay = delay
|
||||
if bg_color: self.frame.configure(fg_color=bg_color)
|
||||
|
||||
self.messageVar.set(message)
|
||||
self.message_label.configure(**kwargs)
|
||||
@@ -0,0 +1,448 @@
|
||||
"""
|
||||
CustomTkinter Messagebox
|
||||
Author: Akash Bora
|
||||
Version: 2.5
|
||||
"""
|
||||
|
||||
import customtkinter
|
||||
from PIL import Image, ImageTk
|
||||
import os
|
||||
import sys
|
||||
import time
|
||||
from typing import Literal
|
||||
|
||||
class CTkMessagebox(customtkinter.CTkToplevel):
|
||||
ICONS = {
|
||||
"check": None,
|
||||
"cancel": None,
|
||||
"info": None,
|
||||
"question": None,
|
||||
"warning": None
|
||||
}
|
||||
ICON_BITMAP = {}
|
||||
def __init__(self,
|
||||
master: any = None,
|
||||
width: int = 400,
|
||||
height: int = 200,
|
||||
title: str = "CTkMessagebox",
|
||||
message: str = "This is a CTkMessagebox!",
|
||||
option_1: str = "OK",
|
||||
option_2: str = None,
|
||||
option_3: str = None,
|
||||
options: list = [],
|
||||
border_width: int = 1,
|
||||
border_color: str = "default",
|
||||
button_color: str = "default",
|
||||
bg_color: str = "default",
|
||||
fg_color: str = "default",
|
||||
text_color: str = "default",
|
||||
title_color: str = "default",
|
||||
button_text_color: str = "default",
|
||||
button_width: int = None,
|
||||
button_height: int = None,
|
||||
cancel_button_color: str = None,
|
||||
cancel_button: str = None, # types: circle, cross or none
|
||||
button_hover_color: str = "default",
|
||||
icon: str = "info",
|
||||
icon_size: tuple = None,
|
||||
corner_radius: int = 15,
|
||||
justify: str = "right",
|
||||
font: tuple = None,
|
||||
header: bool = False,
|
||||
topmost: bool = True,
|
||||
fade_in_duration: int = 0,
|
||||
sound: bool = False,
|
||||
option_focus: Literal[1, 2, 3] = None):
|
||||
|
||||
super().__init__()
|
||||
|
||||
|
||||
self.master_window = master
|
||||
|
||||
self.width = 250 if width<250 else width
|
||||
self.height = 150 if height<150 else height
|
||||
|
||||
if self.master_window is None:
|
||||
self.spawn_x = int((self.winfo_screenwidth()-self.width)/2)
|
||||
self.spawn_y = int((self.winfo_screenheight()-self.height)/2)
|
||||
else:
|
||||
self.spawn_x = int(self.master_window.winfo_width() * .5 + self.master_window.winfo_x() - .5 * self.width + 7)
|
||||
self.spawn_y = int(self.master_window.winfo_height() * .5 + self.master_window.winfo_y() - .5 * self.height + 20)
|
||||
|
||||
self.after(10)
|
||||
self.geometry(f"{self.width}x{self.height}+{self.spawn_x}+{self.spawn_y}")
|
||||
self.title(title)
|
||||
self.resizable(width=False, height=False)
|
||||
self.fade = fade_in_duration
|
||||
|
||||
if self.fade:
|
||||
self.fade = 20 if self.fade<20 else self.fade
|
||||
self.attributes("-alpha", 0)
|
||||
|
||||
if not header:
|
||||
self.overrideredirect(1)
|
||||
|
||||
if topmost:
|
||||
self.attributes("-topmost", True)
|
||||
else:
|
||||
self.transient(self.master_window)
|
||||
|
||||
if sys.platform.startswith("win"):
|
||||
self.transparent_color = self._apply_appearance_mode(self.cget("fg_color"))
|
||||
self.attributes("-transparentcolor", self.transparent_color)
|
||||
default_cancel_button = "cross"
|
||||
elif sys.platform.startswith("darwin"):
|
||||
self.transparent_color = 'systemTransparent'
|
||||
self.attributes("-transparent", True)
|
||||
default_cancel_button = "circle"
|
||||
else:
|
||||
self.transparent_color = '#000001'
|
||||
corner_radius = 0
|
||||
default_cancel_button = "cross"
|
||||
|
||||
self.lift()
|
||||
|
||||
self.config(background=self.transparent_color)
|
||||
self.protocol("WM_DELETE_WINDOW", self.button_event)
|
||||
self.grid_columnconfigure(0, weight=1)
|
||||
self.grid_rowconfigure(0, weight=1)
|
||||
self.x = self.winfo_x()
|
||||
self.y = self.winfo_y()
|
||||
self._title = title
|
||||
self.message = message
|
||||
self.font = font
|
||||
self.justify = justify
|
||||
self.sound = sound
|
||||
self.cancel_button = cancel_button if cancel_button else default_cancel_button
|
||||
self.round_corners = corner_radius if corner_radius<=30 else 30
|
||||
self.button_width = button_width if button_width else self.width/4
|
||||
self.button_height = button_height if button_height else 28
|
||||
|
||||
if self.fade: self.attributes("-alpha", 0)
|
||||
|
||||
if self.button_height>self.height/4: self.button_height = self.height/4 -20
|
||||
self.dot_color = cancel_button_color
|
||||
self.border_width = border_width if border_width<6 else 5
|
||||
|
||||
if type(options) is list and len(options)>0:
|
||||
try:
|
||||
option_1 = options[-1]
|
||||
option_2 = options[-2]
|
||||
option_3 = options[-3]
|
||||
except IndexError: None
|
||||
|
||||
if bg_color=="default":
|
||||
self.bg_color = self._apply_appearance_mode(customtkinter.ThemeManager.theme["CTkFrame"]["fg_color"])
|
||||
else:
|
||||
self.bg_color = bg_color
|
||||
|
||||
if fg_color=="default":
|
||||
self.fg_color = self._apply_appearance_mode(customtkinter.ThemeManager.theme["CTkFrame"]["top_fg_color"])
|
||||
else:
|
||||
self.fg_color = fg_color
|
||||
|
||||
default_button_color = self._apply_appearance_mode(customtkinter.ThemeManager.theme["CTkButton"]["fg_color"])
|
||||
|
||||
if sys.platform.startswith("win"):
|
||||
if self.bg_color==self.transparent_color or self.fg_color==self.transparent_color:
|
||||
self.configure(fg_color="#000001")
|
||||
self.transparent_color = "#000001"
|
||||
self.attributes("-transparentcolor", self.transparent_color)
|
||||
|
||||
if button_color=="default":
|
||||
self.button_color = (default_button_color, default_button_color, default_button_color)
|
||||
else:
|
||||
if type(button_color) is tuple:
|
||||
if len(button_color)==2:
|
||||
self.button_color = (button_color[0], button_color[1], default_button_color)
|
||||
elif len(button_color)==1:
|
||||
self.button_color = (button_color[0], default_button_color, default_button_color)
|
||||
else:
|
||||
self.button_color = button_color
|
||||
else:
|
||||
self.button_color = (button_color, button_color, button_color)
|
||||
|
||||
if text_color=="default":
|
||||
self.text_color = self._apply_appearance_mode(customtkinter.ThemeManager.theme["CTkLabel"]["text_color"])
|
||||
else:
|
||||
self.text_color = text_color
|
||||
|
||||
if title_color=="default":
|
||||
self.title_color = self._apply_appearance_mode(customtkinter.ThemeManager.theme["CTkLabel"]["text_color"])
|
||||
else:
|
||||
self.title_color = title_color
|
||||
|
||||
if button_text_color=="default":
|
||||
self.bt_text_color = self._apply_appearance_mode(customtkinter.ThemeManager.theme["CTkButton"]["text_color"])
|
||||
else:
|
||||
self.bt_text_color = button_text_color
|
||||
|
||||
if button_hover_color=="default":
|
||||
self.bt_hv_color = self._apply_appearance_mode(customtkinter.ThemeManager.theme["CTkButton"]["hover_color"])
|
||||
else:
|
||||
self.bt_hv_color = button_hover_color
|
||||
|
||||
if border_color=="default":
|
||||
self.border_color = self._apply_appearance_mode(customtkinter.ThemeManager.theme["CTkFrame"]["border_color"])
|
||||
else:
|
||||
self.border_color = border_color
|
||||
|
||||
if icon_size:
|
||||
self.size_height = icon_size[1] if icon_size[1]<=self.height-100 else self.height-100
|
||||
self.size = (icon_size[0], self.size_height)
|
||||
else:
|
||||
self.size = (self.height/4, self.height/4)
|
||||
|
||||
self.icon = self.load_icon(icon, icon_size) if icon else None
|
||||
|
||||
self.frame_top = customtkinter.CTkFrame(self, corner_radius=self.round_corners, width=self.width, border_width=self.border_width,
|
||||
bg_color=self.transparent_color, fg_color=self.bg_color, border_color=self.border_color)
|
||||
self.frame_top.grid(sticky="nswe")
|
||||
|
||||
if button_width:
|
||||
self.frame_top.grid_columnconfigure(0, weight=1)
|
||||
else:
|
||||
self.frame_top.grid_columnconfigure((1,2,3), weight=1)
|
||||
|
||||
if button_height:
|
||||
self.frame_top.grid_rowconfigure((0,1,3), weight=1)
|
||||
else:
|
||||
self.frame_top.grid_rowconfigure((0,1,2), weight=1)
|
||||
|
||||
self.frame_top.bind("<B1-Motion>", self.move_window)
|
||||
self.frame_top.bind("<ButtonPress-1>", self.oldxyset)
|
||||
|
||||
if self.cancel_button=="cross":
|
||||
self.button_close = customtkinter.CTkButton(self.frame_top, corner_radius=10, width=0, height=0, hover=False, border_width=0,
|
||||
text_color=self.dot_color if self.dot_color else self.title_color,
|
||||
text="✕", fg_color="transparent", command=self.button_event)
|
||||
self.button_close.grid(row=0, column=5, sticky="ne", padx=5+self.border_width, pady=5+self.border_width)
|
||||
elif self.cancel_button=="circle":
|
||||
self.button_close = customtkinter.CTkButton(self.frame_top, corner_radius=10, width=10, height=10, hover=False, border_width=0,
|
||||
text="", fg_color=self.dot_color if self.dot_color else "#c42b1c", command=self.button_event)
|
||||
self.button_close.grid(row=0, column=5, sticky="ne", padx=10, pady=10)
|
||||
|
||||
self.title_label = customtkinter.CTkLabel(self.frame_top, width=1, text=self._title, text_color=self.title_color, font=self.font)
|
||||
self.title_label.grid(row=0, column=0, columnspan=6, sticky="nw", padx=(15,30), pady=5)
|
||||
self.title_label.bind("<B1-Motion>", self.move_window)
|
||||
self.title_label.bind("<ButtonPress-1>", self.oldxyset)
|
||||
|
||||
self.info = customtkinter.CTkButton(self.frame_top, width=1, height=self.height/2, corner_radius=0, text=self.message, font=self.font,
|
||||
fg_color=self.fg_color, hover=False, text_color=self.text_color, image=self.icon)
|
||||
self.info._text_label.configure(wraplength=self.width/2, justify="left")
|
||||
self.info.grid(row=1, column=0, columnspan=6, sticky="nwes", padx=self.border_width)
|
||||
|
||||
if self.info._text_label.winfo_reqheight()>self.height/2:
|
||||
height_offset = int((self.info._text_label.winfo_reqheight())-(self.height/2) + self.height)
|
||||
self.geometry(f"{self.width}x{height_offset}")
|
||||
|
||||
|
||||
self.option_text_1 = option_1
|
||||
|
||||
self.button_1 = customtkinter.CTkButton(self.frame_top, text=self.option_text_1, fg_color=self.button_color[0],
|
||||
width=self.button_width, font=self.font, text_color=self.bt_text_color,
|
||||
hover_color=self.bt_hv_color, height=self.button_height,
|
||||
command=lambda: self.button_event(self.option_text_1))
|
||||
|
||||
|
||||
self.option_text_2 = option_2
|
||||
if option_2:
|
||||
self.button_2 = customtkinter.CTkButton(self.frame_top, text=self.option_text_2, fg_color=self.button_color[1],
|
||||
width=self.button_width, font=self.font, text_color=self.bt_text_color,
|
||||
hover_color=self.bt_hv_color, height=self.button_height,
|
||||
command=lambda: self.button_event(self.option_text_2))
|
||||
|
||||
self.option_text_3 = option_3
|
||||
if option_3:
|
||||
self.button_3 = customtkinter.CTkButton(self.frame_top, text=self.option_text_3, fg_color=self.button_color[2],
|
||||
width=self.button_width, font=self.font, text_color=self.bt_text_color,
|
||||
hover_color=self.bt_hv_color, height=self.button_height,
|
||||
command=lambda: self.button_event(self.option_text_3))
|
||||
|
||||
if self.justify=="center":
|
||||
if button_width:
|
||||
columns = [4,3,2]
|
||||
span = 1
|
||||
else:
|
||||
columns = [4,2,0]
|
||||
span = 2
|
||||
if option_3:
|
||||
self.frame_top.columnconfigure((0,1,2,3,4,5), weight=1)
|
||||
self.button_1.grid(row=2, column=columns[0], columnspan=span, sticky="news", padx=(0,10), pady=10)
|
||||
self.button_2.grid(row=2, column=columns[1], columnspan=span, sticky="news", padx=10, pady=10)
|
||||
self.button_3.grid(row=2, column=columns[2], columnspan=span, sticky="news", padx=(10,0), pady=10)
|
||||
elif option_2:
|
||||
self.frame_top.columnconfigure((0,5), weight=1)
|
||||
columns = [2,3]
|
||||
self.button_1.grid(row=2, column=columns[0], sticky="news", padx=(0,5), pady=10)
|
||||
self.button_2.grid(row=2, column=columns[1], sticky="news", padx=(5,0), pady=10)
|
||||
else:
|
||||
if button_width:
|
||||
self.frame_top.columnconfigure((0,1,2,3,4,5), weight=1)
|
||||
else:
|
||||
self.frame_top.columnconfigure((0,2,4), weight=2)
|
||||
self.button_1.grid(row=2, column=columns[1], columnspan=span, sticky="news", padx=(0,10), pady=10)
|
||||
elif self.justify=="left":
|
||||
self.frame_top.columnconfigure((0,1,2,3,4,5), weight=1)
|
||||
if button_width:
|
||||
columns = [0,1,2]
|
||||
span = 1
|
||||
else:
|
||||
columns = [0,2,4]
|
||||
span = 2
|
||||
if option_3:
|
||||
self.button_1.grid(row=2, column=columns[2], columnspan=span, sticky="news", padx=(0,10), pady=10)
|
||||
self.button_2.grid(row=2, column=columns[1], columnspan=span, sticky="news", padx=10, pady=10)
|
||||
self.button_3.grid(row=2, column=columns[0], columnspan=span, sticky="news", padx=(10,0), pady=10)
|
||||
elif option_2:
|
||||
self.button_1.grid(row=2, column=columns[1], columnspan=span, sticky="news", padx=10, pady=10)
|
||||
self.button_2.grid(row=2, column=columns[0], columnspan=span, sticky="news", padx=(10,0), pady=10)
|
||||
else:
|
||||
self.button_1.grid(row=2, column=columns[0], columnspan=span, sticky="news", padx=(10,0), pady=10)
|
||||
else:
|
||||
self.frame_top.columnconfigure((0,1,2,3,4,5), weight=1)
|
||||
if button_width:
|
||||
columns = [5,4,3]
|
||||
span = 1
|
||||
else:
|
||||
columns = [4,2,0]
|
||||
span = 2
|
||||
self.button_1.grid(row=2, column=columns[0], columnspan=span, sticky="news", padx=(0,10), pady=10)
|
||||
if option_2:
|
||||
self.button_2.grid(row=2, column=columns[1], columnspan=span, sticky="news", padx=10, pady=10)
|
||||
if option_3:
|
||||
self.button_3.grid(row=2, column=columns[2], columnspan=span, sticky="news", padx=(10,0), pady=10)
|
||||
|
||||
if header:
|
||||
self.title_label.configure(text="")
|
||||
self.title_label.grid_configure(pady=0)
|
||||
self.button_close.configure(text_color=self.bg_color)
|
||||
self.frame_top.configure(corner_radius=0)
|
||||
|
||||
if self.winfo_exists():
|
||||
self.grab_set()
|
||||
|
||||
if self.sound:
|
||||
self.bell()
|
||||
|
||||
if self.fade:
|
||||
self.fade_in()
|
||||
|
||||
if option_focus:
|
||||
self.option_focus = option_focus
|
||||
self.focus_button(self.option_focus)
|
||||
else:
|
||||
if not self.option_text_2 and not self.option_text_3:
|
||||
self.button_1.focus()
|
||||
self.button_1.bind("<Return>", lambda event: self.button_event(self.option_text_1))
|
||||
|
||||
self.bind("<Escape>", lambda e: self.button_event())
|
||||
|
||||
def focus_button(self, option_focus):
|
||||
try:
|
||||
self.selected_button = getattr(self, "button_"+str(option_focus))
|
||||
self.selected_button.focus()
|
||||
self.selected_button.configure(border_color=self.bt_hv_color, border_width=3)
|
||||
self.selected_option = getattr(self, "option_text_"+str(option_focus))
|
||||
self.selected_button.bind("<Return>", lambda event: self.button_event(self.selected_option))
|
||||
except AttributeError:
|
||||
return
|
||||
|
||||
self.bind("<Left>", lambda e: self.change_left())
|
||||
self.bind("<Right>", lambda e: self.change_right())
|
||||
|
||||
def change_left(self):
|
||||
if self.option_focus==3:
|
||||
return
|
||||
|
||||
self.selected_button.unbind("<Return>")
|
||||
self.selected_button.configure(border_width=0)
|
||||
|
||||
if self.option_focus==1:
|
||||
if self.option_text_2:
|
||||
self.option_focus = 2
|
||||
|
||||
elif self.option_focus==2:
|
||||
if self.option_text_3:
|
||||
self.option_focus = 3
|
||||
|
||||
self.focus_button(self.option_focus)
|
||||
|
||||
def change_right(self):
|
||||
if self.option_focus==1:
|
||||
return
|
||||
|
||||
self.selected_button.unbind("<Return>")
|
||||
self.selected_button.configure(border_width=0)
|
||||
|
||||
if self.option_focus==2:
|
||||
self.option_focus = 1
|
||||
|
||||
elif self.option_focus==3:
|
||||
self.option_focus = 2
|
||||
|
||||
self.focus_button(self.option_focus)
|
||||
|
||||
def load_icon(self, icon, icon_size):
|
||||
if icon not in self.ICONS or self.ICONS[icon] is None:
|
||||
if icon in ["check", "cancel", "info", "question", "warning"]:
|
||||
image_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'icons', icon + '.png')
|
||||
else:
|
||||
image_path = icon
|
||||
if icon_size:
|
||||
size_height = icon_size[1] if icon_size[1] <= self.height - 100 else self.height - 100
|
||||
size = (icon_size[0], size_height)
|
||||
else:
|
||||
size = (self.height / 4, self.height / 4)
|
||||
self.ICONS[icon] = customtkinter.CTkImage(Image.open(image_path), size=size)
|
||||
self.ICON_BITMAP[icon] = ImageTk.PhotoImage(file=image_path)
|
||||
self.after(200, lambda: self.iconphoto(False, self.ICON_BITMAP[icon]))
|
||||
return self.ICONS[icon]
|
||||
|
||||
def fade_in(self):
|
||||
for i in range(0,110,10):
|
||||
if not self.winfo_exists():
|
||||
break
|
||||
self.attributes("-alpha", i/100)
|
||||
self.update()
|
||||
time.sleep(1/self.fade)
|
||||
|
||||
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/self.fade)
|
||||
|
||||
def get(self):
|
||||
if self.winfo_exists():
|
||||
self.master.wait_window(self)
|
||||
return self.event
|
||||
|
||||
def oldxyset(self, event):
|
||||
self.oldx = event.x
|
||||
self.oldy = event.y
|
||||
|
||||
def move_window(self, event):
|
||||
self.y = event.y_root - self.oldy
|
||||
self.x = event.x_root - self.oldx
|
||||
self.geometry(f'+{self.x}+{self.y}')
|
||||
|
||||
def button_event(self, event=None):
|
||||
try:
|
||||
self.button_1.configure(state="disabled")
|
||||
self.button_2.configure(state="disabled")
|
||||
self.button_3.configure(state="disabled")
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
if self.fade:
|
||||
self.fade_out()
|
||||
self.grab_release()
|
||||
self.destroy()
|
||||
self.event = event
|
||||
|
||||
if __name__ == "__main__":
|
||||
app = CTkMessagebox()
|
||||
app.mainloop()
|
||||
|
After Width: | Height: | Size: 38 KiB |
|
After Width: | Height: | Size: 31 KiB |
|
After Width: | Height: | Size: 15 KiB |
|
After Width: | Height: | Size: 15 KiB |
|
After Width: | Height: | Size: 17 KiB |
@@ -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)"
|
||||
]
|
||||
@@ -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"
|
||||
]
|
||||
@@ -0,0 +1,109 @@
|
||||
import customtkinter
|
||||
import json
|
||||
import sys
|
||||
import os
|
||||
|
||||
class Config:
|
||||
def __init__(self, linker, config_file):
|
||||
self.default_config = {
|
||||
"ResetDaily": False,
|
||||
"LastRun": "2023-12-24 21:41:55",
|
||||
"ResetTime": "11:21:30",
|
||||
"RechargeAP": False,
|
||||
"PreferredTemplate": "template1",
|
||||
"Queue": [],
|
||||
"Event": False,
|
||||
"Templates": {
|
||||
"template1": []
|
||||
}
|
||||
}
|
||||
self.linker = linker
|
||||
self.config_file = config_file
|
||||
|
||||
if not os.path.exists(self.config_file):
|
||||
with open(self.config_file, "w") as f:
|
||||
json.dump(self.default_config, f, indent=2)
|
||||
|
||||
self.config_data = self.read()
|
||||
self.linker.widgets = self.set_values_to_none(self.config_data)
|
||||
self.locked = False
|
||||
linker.config = self
|
||||
|
||||
def read(self):
|
||||
# Read the JSON file
|
||||
try:
|
||||
with open(self.config_file, 'r') as json_file:
|
||||
config_data = json.load(json_file)
|
||||
return config_data
|
||||
except FileNotFoundError:
|
||||
print(f"Config file '{self.config_file}' not found.")
|
||||
sys.exit(1)
|
||||
except json.JSONDecodeError:
|
||||
print(f"Invalid JSON format in '{self.config_file}'.")
|
||||
sys.exit(1)
|
||||
|
||||
def set_values_to_none(self, input_dict):
|
||||
result = {}
|
||||
for key, value in input_dict.items():
|
||||
if isinstance(value, dict):
|
||||
result[key] = self.set_values_to_none(value)
|
||||
else:
|
||||
result[key] = None
|
||||
return result
|
||||
|
||||
def load_config(self, widgets=None, config_data=None):
|
||||
if widgets == None:
|
||||
widgets = self.linker.widgets
|
||||
config_data = self.config_data
|
||||
for key in widgets:
|
||||
if isinstance(widgets[key], dict) and isinstance(config_data[key], dict):
|
||||
self.load_config(widgets[key], config_data[key])
|
||||
else:
|
||||
if widgets[key] is not None:
|
||||
if isinstance(widgets[key], customtkinter.CTkCheckBox):
|
||||
if config_data[key] == True:
|
||||
widgets[key].select()
|
||||
else:
|
||||
widgets[key].deselect()
|
||||
elif isinstance(widgets[key], customtkinter.CTkEntry):
|
||||
widgets[key].insert(0, config_data[key])
|
||||
else:
|
||||
widgets[key].set(config_data[key])
|
||||
|
||||
def save_to_json(self, list_keys):
|
||||
widget = self.linker.widgets
|
||||
data = self.config_data
|
||||
for i in list_keys[:-1]:
|
||||
widget = widget[i]
|
||||
data = data[i]
|
||||
widget = widget[list_keys[-1]]
|
||||
value = widget.get()
|
||||
if isinstance(widget, customtkinter.CTkCheckBox):
|
||||
value = True if value==1 else False
|
||||
data[list_keys[-1]] = value
|
||||
self.save_file("Configuration")
|
||||
|
||||
def save_file(self, name=None):
|
||||
if self.locked:
|
||||
with open("MCE\config.json", "r") as config_file:
|
||||
new_config = json.load(config_file)
|
||||
self.config_data["Queue"] = new_config["Queue"]
|
||||
self.config_data["LastRun"] = new_config["LastRun"]
|
||||
with open("MCE\config.json", "w") as config_file:
|
||||
json.dump(self.config_data, config_file, indent=2)
|
||||
if name:
|
||||
self.linker.show_notification(name)
|
||||
|
||||
class Linker:
|
||||
def __init__(self):
|
||||
self.capitalise = lambda word: " ".join(x.title() for x in word.split("_"))
|
||||
self.config = None
|
||||
self.widgets = {}
|
||||
self.sidebar = None
|
||||
self.event_id = None
|
||||
|
||||
def show_notification(self, text):
|
||||
if self.event_id:
|
||||
self.sidebar.after_cancel(self.event_id)
|
||||
self.sidebar.notification.show()
|
||||
self.event_id = self.sidebar.after(2500, self.sidebar.notification.hide)
|
||||
@@ -46,11 +46,30 @@ class ArisuAutoSweeper(AzurLaneAutoScript):
|
||||
from tasks.tactical_challenge.tactical_challenge import TacticalChallenge
|
||||
TacticalChallenge(config=self.config, device=self.device).run()
|
||||
|
||||
def task(self):
|
||||
from tasks.task.task import Task
|
||||
Task(config=self.config, device=self.device).run()
|
||||
|
||||
def shop(self):
|
||||
from tasks.shop.shop import Shop
|
||||
Shop(config=self.config, device=self.device).run()
|
||||
|
||||
def momotalk(self):
|
||||
from tasks.momotalk.momotalk import MomoTalk
|
||||
MomoTalk(config=self.config, device=self.device).run()
|
||||
|
||||
def mission(self):
|
||||
from tasks.mission.mission import Mission
|
||||
Mission(config=self.config, device=self.device).run()
|
||||
|
||||
def schedule(self):
|
||||
from tasks.schedule.schedule import Schedule
|
||||
Schedule(config=self.config, device=self.device).run()
|
||||
|
||||
def data_update(self):
|
||||
from tasks.item.data_update import DataUpdate
|
||||
DataUpdate(config=self.config, device=self.device).run()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
aas = ArisuAutoSweeper('aas')
|
||||
aas.loop()
|
||||
|
||||
|
After Width: | Height: | Size: 6.5 KiB |
|
After Width: | Height: | Size: 18 KiB |
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 28 KiB |
|
After Width: | Height: | Size: 15 KiB |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 5.6 KiB |
|
After Width: | Height: | Size: 14 KiB |
|
After Width: | Height: | Size: 5.9 KiB |
|
After Width: | Height: | Size: 5.3 KiB |
|
After Width: | Height: | Size: 8.6 KiB |
|
After Width: | Height: | Size: 6.2 KiB |
|
After Width: | Height: | Size: 6.8 KiB |
|
After Width: | Height: | Size: 5.5 KiB |
|
After Width: | Height: | Size: 7.1 KiB |
|
After Width: | Height: | Size: 7.9 KiB |
|
After Width: | Height: | Size: 8.1 KiB |
|
After Width: | Height: | Size: 14 KiB |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 10 KiB |
|
After Width: | Height: | Size: 51 KiB |
|
After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 5.9 KiB |
|
After Width: | Height: | Size: 4.4 KiB |
|
After Width: | Height: | Size: 7.1 KiB |
|
After Width: | Height: | Size: 4.8 KiB |
|
After Width: | Height: | Size: 6.0 KiB |
|
After Width: | Height: | Size: 7.4 KiB |
|
After Width: | Height: | Size: 5.4 KiB |
|
After Width: | Height: | Size: 4.7 KiB |
|
After Width: | Height: | Size: 4.8 KiB |
|
After Width: | Height: | Size: 7.8 KiB |
|
After Width: | Height: | Size: 4.8 KiB |
|
After Width: | Height: | Size: 7.8 KiB |
|
After Width: | Height: | Size: 5.5 KiB |
|
After Width: | Height: | Size: 6.3 KiB |
|
After Width: | Height: | Size: 5.7 KiB |
|
After Width: | Height: | Size: 5.6 KiB |
|
After Width: | Height: | Size: 5.8 KiB |
|
After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 69 KiB |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 8.0 KiB |
|
After Width: | Height: | Size: 5.8 KiB |
|
After Width: | Height: | Size: 13 KiB |
|
After Width: | Height: | Size: 9.8 KiB |
|
After Width: | Height: | Size: 18 KiB |
|
After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 10 KiB |
|
After Width: | Height: | Size: 274 KiB |
|
After Width: | Height: | Size: 7.2 KiB |
|
After Width: | Height: | Size: 8.2 KiB |
|
After Width: | Height: | Size: 5.7 KiB |
|
After Width: | Height: | Size: 9.4 KiB |
|
After Width: | Height: | Size: 6.9 KiB |
|
After Width: | Height: | Size: 9.1 KiB |
|
After Width: | Height: | Size: 9.7 KiB |
|
After Width: | Height: | Size: 7.9 KiB |
|
After Width: | Height: | Size: 6.6 KiB |
|
After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 10 KiB |
|
After Width: | Height: | Size: 7.9 KiB |
|
After Width: | Height: | Size: 7.8 KiB |
|
After Width: | Height: | Size: 6.9 KiB |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 11 KiB |
@@ -33,6 +33,22 @@
|
||||
"ServerUpdate": "04:00"
|
||||
}
|
||||
},
|
||||
"DataUpdate": {
|
||||
"Scheduler": {
|
||||
"Enable": true,
|
||||
"NextRun": "2020-01-01 00:00:00",
|
||||
"Command": "DataUpdate",
|
||||
"ServerUpdate": "04:00"
|
||||
},
|
||||
"ItemStorage": {
|
||||
"AP": {},
|
||||
"Credit": {},
|
||||
"Pyroxene": {},
|
||||
"BountyTicket": {},
|
||||
"ScrimmageTicket": {},
|
||||
"TacticalChallengeTicket": {}
|
||||
}
|
||||
},
|
||||
"Cafe": {
|
||||
"Scheduler": {
|
||||
"Enable": true,
|
||||
@@ -54,20 +70,86 @@
|
||||
"Substitute": false
|
||||
}
|
||||
},
|
||||
"Circle": {
|
||||
"Schedule": {
|
||||
"Scheduler": {
|
||||
"Enable": true,
|
||||
"Enable": false,
|
||||
"NextRun": "2020-01-01 00:00:00",
|
||||
"Command": "Circle",
|
||||
"Command": "Schedule",
|
||||
"ServerUpdate": "04:00"
|
||||
},
|
||||
"Schedule": {
|
||||
"OnError": "skip"
|
||||
},
|
||||
"Choice1": {
|
||||
"Location": "None",
|
||||
"Classrooms": null
|
||||
},
|
||||
"Choice2": {
|
||||
"Location": "None",
|
||||
"Classrooms": null
|
||||
},
|
||||
"Choice3": {
|
||||
"Location": "None",
|
||||
"Classrooms": null
|
||||
},
|
||||
"Choice4": {
|
||||
"Location": "None",
|
||||
"Classrooms": null
|
||||
},
|
||||
"Choice5": {
|
||||
"Location": "None",
|
||||
"Classrooms": null
|
||||
}
|
||||
},
|
||||
"Mail": {
|
||||
"Shop": {
|
||||
"Scheduler": {
|
||||
"Enable": true,
|
||||
"Enable": false,
|
||||
"NextRun": "2020-01-01 00:00:00",
|
||||
"Command": "Mail",
|
||||
"Command": "Shop",
|
||||
"ServerUpdate": "04:00"
|
||||
},
|
||||
"NormalShop": {
|
||||
"Enable": false,
|
||||
"Purchases": 1,
|
||||
"1": false,
|
||||
"2": false,
|
||||
"3": false,
|
||||
"4": false,
|
||||
"5": false,
|
||||
"6": false,
|
||||
"7": false,
|
||||
"8": false,
|
||||
"9": false,
|
||||
"10": false,
|
||||
"11": false,
|
||||
"12": false,
|
||||
"13": false,
|
||||
"14": false,
|
||||
"15": false,
|
||||
"16": false,
|
||||
"17": false,
|
||||
"18": false,
|
||||
"19": false,
|
||||
"20": false
|
||||
},
|
||||
"TacticalChallengeShop": {
|
||||
"Enable": false,
|
||||
"Purchases": 1,
|
||||
"1": false,
|
||||
"2": false,
|
||||
"3": false,
|
||||
"4": false,
|
||||
"5": false,
|
||||
"6": false,
|
||||
"7": false,
|
||||
"8": false,
|
||||
"9": false,
|
||||
"10": false,
|
||||
"11": false,
|
||||
"12": false,
|
||||
"13": false,
|
||||
"14": false,
|
||||
"15": false
|
||||
}
|
||||
},
|
||||
"Bounty": {
|
||||
@@ -121,26 +203,50 @@
|
||||
"Enable": true,
|
||||
"NextRun": "2020-01-01 00:00:00",
|
||||
"Command": "TacticalChallenge",
|
||||
"ServerUpdate": "15:00"
|
||||
"ServerUpdate": "14:00"
|
||||
},
|
||||
"TacticalChallenge": {
|
||||
"PlayerSelect": 0
|
||||
}
|
||||
},
|
||||
"DataUpdate": {
|
||||
"Mission": {
|
||||
"Scheduler": {
|
||||
"Enable": false,
|
||||
"NextRun": "2020-01-01 00:00:00",
|
||||
"Command": "Mission",
|
||||
"ServerUpdate": "04:00"
|
||||
}
|
||||
},
|
||||
"Circle": {
|
||||
"Scheduler": {
|
||||
"Enable": true,
|
||||
"NextRun": "2020-01-01 00:00:00",
|
||||
"Command": "DataUpdate",
|
||||
"Command": "Circle",
|
||||
"ServerUpdate": "04:00"
|
||||
}
|
||||
},
|
||||
"Task": {
|
||||
"Scheduler": {
|
||||
"Enable": false,
|
||||
"NextRun": "2020-01-01 00:00:00",
|
||||
"Command": "Task",
|
||||
"ServerUpdate": "04:00"
|
||||
}
|
||||
},
|
||||
"Mail": {
|
||||
"Scheduler": {
|
||||
"Enable": true,
|
||||
"NextRun": "2020-01-01 00:00:00",
|
||||
"Command": "Mail",
|
||||
"ServerUpdate": "04:00"
|
||||
}
|
||||
},
|
||||
"Momotalk": {
|
||||
"Scheduler": {
|
||||
"Enable": false,
|
||||
"NextRun": "2020-01-01 00:00:00",
|
||||
"Command": "Momotalk",
|
||||
"ServerUpdate": "04:00"
|
||||
},
|
||||
"ItemStorage": {
|
||||
"AP": {},
|
||||
"Credit": {},
|
||||
"Pyroxene": {},
|
||||
"BountyTicket": {},
|
||||
"ScrimmageTicket": {},
|
||||
"TacticalChallengeTicket": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2015-present Electron React Boilerplate
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
@@ -0,0 +1,121 @@
|
||||
Creative Commons Legal Code
|
||||
|
||||
CC0 1.0 Universal
|
||||
|
||||
CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE
|
||||
LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN
|
||||
ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS
|
||||
INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES
|
||||
REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS
|
||||
PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM
|
||||
THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED
|
||||
HEREUNDER.
|
||||
|
||||
Statement of Purpose
|
||||
|
||||
The laws of most jurisdictions throughout the world automatically confer
|
||||
exclusive Copyright and Related Rights (defined below) upon the creator
|
||||
and subsequent owner(s) (each and all, an "owner") of an original work of
|
||||
authorship and/or a database (each, a "Work").
|
||||
|
||||
Certain owners wish to permanently relinquish those rights to a Work for
|
||||
the purpose of contributing to a commons of creative, cultural and
|
||||
scientific works ("Commons") that the public can reliably and without fear
|
||||
of later claims of infringement build upon, modify, incorporate in other
|
||||
works, reuse and redistribute as freely as possible in any form whatsoever
|
||||
and for any purposes, including without limitation commercial purposes.
|
||||
These owners may contribute to the Commons to promote the ideal of a free
|
||||
culture and the further production of creative, cultural and scientific
|
||||
works, or to gain reputation or greater distribution for their Work in
|
||||
part through the use and efforts of others.
|
||||
|
||||
For these and/or other purposes and motivations, and without any
|
||||
expectation of additional consideration or compensation, the person
|
||||
associating CC0 with a Work (the "Affirmer"), to the extent that he or she
|
||||
is an owner of Copyright and Related Rights in the Work, voluntarily
|
||||
elects to apply CC0 to the Work and publicly distribute the Work under its
|
||||
terms, with knowledge of his or her Copyright and Related Rights in the
|
||||
Work and the meaning and intended legal effect of CC0 on those rights.
|
||||
|
||||
1. Copyright and Related Rights. A Work made available under CC0 may be
|
||||
protected by copyright and related or neighboring rights ("Copyright and
|
||||
Related Rights"). Copyright and Related Rights include, but are not
|
||||
limited to, the following:
|
||||
|
||||
i. the right to reproduce, adapt, distribute, perform, display,
|
||||
communicate, and translate a Work;
|
||||
ii. moral rights retained by the original author(s) and/or performer(s);
|
||||
iii. publicity and privacy rights pertaining to a person's image or
|
||||
likeness depicted in a Work;
|
||||
iv. rights protecting against unfair competition in regards to a Work,
|
||||
subject to the limitations in paragraph 4(a), below;
|
||||
v. rights protecting the extraction, dissemination, use and reuse of data
|
||||
in a Work;
|
||||
vi. database rights (such as those arising under Directive 96/9/EC of the
|
||||
European Parliament and of the Council of 11 March 1996 on the legal
|
||||
protection of databases, and under any national implementation
|
||||
thereof, including any amended or successor version of such
|
||||
directive); and
|
||||
vii. other similar, equivalent or corresponding rights throughout the
|
||||
world based on applicable law or treaty, and any national
|
||||
implementations thereof.
|
||||
|
||||
2. Waiver. To the greatest extent permitted by, but not in contravention
|
||||
of, applicable law, Affirmer hereby overtly, fully, permanently,
|
||||
irrevocably and unconditionally waives, abandons, and surrenders all of
|
||||
Affirmer's Copyright and Related Rights and associated claims and causes
|
||||
of action, whether now known or unknown (including existing as well as
|
||||
future claims and causes of action), in the Work (i) in all territories
|
||||
worldwide, (ii) for the maximum duration provided by applicable law or
|
||||
treaty (including future time extensions), (iii) in any current or future
|
||||
medium and for any number of copies, and (iv) for any purpose whatsoever,
|
||||
including without limitation commercial, advertising or promotional
|
||||
purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each
|
||||
member of the public at large and to the detriment of Affirmer's heirs and
|
||||
successors, fully intending that such Waiver shall not be subject to
|
||||
revocation, rescission, cancellation, termination, or any other legal or
|
||||
equitable action to disrupt the quiet enjoyment of the Work by the public
|
||||
as contemplated by Affirmer's express Statement of Purpose.
|
||||
|
||||
3. Public License Fallback. Should any part of the Waiver for any reason
|
||||
be judged legally invalid or ineffective under applicable law, then the
|
||||
Waiver shall be preserved to the maximum extent permitted taking into
|
||||
account Affirmer's express Statement of Purpose. In addition, to the
|
||||
extent the Waiver is so judged Affirmer hereby grants to each affected
|
||||
person a royalty-free, non transferable, non sublicensable, non exclusive,
|
||||
irrevocable and unconditional license to exercise Affirmer's Copyright and
|
||||
Related Rights in the Work (i) in all territories worldwide, (ii) for the
|
||||
maximum duration provided by applicable law or treaty (including future
|
||||
time extensions), (iii) in any current or future medium and for any number
|
||||
of copies, and (iv) for any purpose whatsoever, including without
|
||||
limitation commercial, advertising or promotional purposes (the
|
||||
"License"). The License shall be deemed effective as of the date CC0 was
|
||||
applied by Affirmer to the Work. Should any part of the License for any
|
||||
reason be judged legally invalid or ineffective under applicable law, such
|
||||
partial invalidity or ineffectiveness shall not invalidate the remainder
|
||||
of the License, and in such case Affirmer hereby affirms that he or she
|
||||
will not (i) exercise any of his or her remaining Copyright and Related
|
||||
Rights in the Work or (ii) assert any associated claims and causes of
|
||||
action with respect to the Work, in either case contrary to Affirmer's
|
||||
express Statement of Purpose.
|
||||
|
||||
4. Limitations and Disclaimers.
|
||||
|
||||
a. No trademark or patent rights held by Affirmer are waived, abandoned,
|
||||
surrendered, licensed or otherwise affected by this document.
|
||||
b. Affirmer offers the Work as-is and makes no representations or
|
||||
warranties of any kind concerning the Work, express, implied,
|
||||
statutory or otherwise, including without limitation warranties of
|
||||
title, merchantability, fitness for a particular purpose, non
|
||||
infringement, or the absence of latent or other defects, accuracy, or
|
||||
the present or absence of errors, whether or not discoverable, all to
|
||||
the greatest extent permissible under applicable law.
|
||||
c. Affirmer disclaims responsibility for clearing rights of other persons
|
||||
that may apply to the Work or any use thereof, including without
|
||||
limitation any person's Copyright and Related Rights in the Work.
|
||||
Further, Affirmer disclaims responsibility for obtaining any necessary
|
||||
consents, permissions or other rights required for any use of the
|
||||
Work.
|
||||
d. Affirmer understands and acknowledges that Creative Commons is not a
|
||||
party to this document and has no duty or obligation with respect to
|
||||
this CC0 or use of the Work.
|
||||
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2023 Akash Bora
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
@@ -0,0 +1,121 @@
|
||||
Creative Commons Legal Code
|
||||
|
||||
CC0 1.0 Universal
|
||||
|
||||
CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE
|
||||
LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN
|
||||
ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS
|
||||
INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES
|
||||
REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS
|
||||
PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM
|
||||
THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED
|
||||
HEREUNDER.
|
||||
|
||||
Statement of Purpose
|
||||
|
||||
The laws of most jurisdictions throughout the world automatically confer
|
||||
exclusive Copyright and Related Rights (defined below) upon the creator
|
||||
and subsequent owner(s) (each and all, an "owner") of an original work of
|
||||
authorship and/or a database (each, a "Work").
|
||||
|
||||
Certain owners wish to permanently relinquish those rights to a Work for
|
||||
the purpose of contributing to a commons of creative, cultural and
|
||||
scientific works ("Commons") that the public can reliably and without fear
|
||||
of later claims of infringement build upon, modify, incorporate in other
|
||||
works, reuse and redistribute as freely as possible in any form whatsoever
|
||||
and for any purposes, including without limitation commercial purposes.
|
||||
These owners may contribute to the Commons to promote the ideal of a free
|
||||
culture and the further production of creative, cultural and scientific
|
||||
works, or to gain reputation or greater distribution for their Work in
|
||||
part through the use and efforts of others.
|
||||
|
||||
For these and/or other purposes and motivations, and without any
|
||||
expectation of additional consideration or compensation, the person
|
||||
associating CC0 with a Work (the "Affirmer"), to the extent that he or she
|
||||
is an owner of Copyright and Related Rights in the Work, voluntarily
|
||||
elects to apply CC0 to the Work and publicly distribute the Work under its
|
||||
terms, with knowledge of his or her Copyright and Related Rights in the
|
||||
Work and the meaning and intended legal effect of CC0 on those rights.
|
||||
|
||||
1. Copyright and Related Rights. A Work made available under CC0 may be
|
||||
protected by copyright and related or neighboring rights ("Copyright and
|
||||
Related Rights"). Copyright and Related Rights include, but are not
|
||||
limited to, the following:
|
||||
|
||||
i. the right to reproduce, adapt, distribute, perform, display,
|
||||
communicate, and translate a Work;
|
||||
ii. moral rights retained by the original author(s) and/or performer(s);
|
||||
iii. publicity and privacy rights pertaining to a person's image or
|
||||
likeness depicted in a Work;
|
||||
iv. rights protecting against unfair competition in regards to a Work,
|
||||
subject to the limitations in paragraph 4(a), below;
|
||||
v. rights protecting the extraction, dissemination, use and reuse of data
|
||||
in a Work;
|
||||
vi. database rights (such as those arising under Directive 96/9/EC of the
|
||||
European Parliament and of the Council of 11 March 1996 on the legal
|
||||
protection of databases, and under any national implementation
|
||||
thereof, including any amended or successor version of such
|
||||
directive); and
|
||||
vii. other similar, equivalent or corresponding rights throughout the
|
||||
world based on applicable law or treaty, and any national
|
||||
implementations thereof.
|
||||
|
||||
2. Waiver. To the greatest extent permitted by, but not in contravention
|
||||
of, applicable law, Affirmer hereby overtly, fully, permanently,
|
||||
irrevocably and unconditionally waives, abandons, and surrenders all of
|
||||
Affirmer's Copyright and Related Rights and associated claims and causes
|
||||
of action, whether now known or unknown (including existing as well as
|
||||
future claims and causes of action), in the Work (i) in all territories
|
||||
worldwide, (ii) for the maximum duration provided by applicable law or
|
||||
treaty (including future time extensions), (iii) in any current or future
|
||||
medium and for any number of copies, and (iv) for any purpose whatsoever,
|
||||
including without limitation commercial, advertising or promotional
|
||||
purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each
|
||||
member of the public at large and to the detriment of Affirmer's heirs and
|
||||
successors, fully intending that such Waiver shall not be subject to
|
||||
revocation, rescission, cancellation, termination, or any other legal or
|
||||
equitable action to disrupt the quiet enjoyment of the Work by the public
|
||||
as contemplated by Affirmer's express Statement of Purpose.
|
||||
|
||||
3. Public License Fallback. Should any part of the Waiver for any reason
|
||||
be judged legally invalid or ineffective under applicable law, then the
|
||||
Waiver shall be preserved to the maximum extent permitted taking into
|
||||
account Affirmer's express Statement of Purpose. In addition, to the
|
||||
extent the Waiver is so judged Affirmer hereby grants to each affected
|
||||
person a royalty-free, non transferable, non sublicensable, non exclusive,
|
||||
irrevocable and unconditional license to exercise Affirmer's Copyright and
|
||||
Related Rights in the Work (i) in all territories worldwide, (ii) for the
|
||||
maximum duration provided by applicable law or treaty (including future
|
||||
time extensions), (iii) in any current or future medium and for any number
|
||||
of copies, and (iv) for any purpose whatsoever, including without
|
||||
limitation commercial, advertising or promotional purposes (the
|
||||
"License"). The License shall be deemed effective as of the date CC0 was
|
||||
applied by Affirmer to the Work. Should any part of the License for any
|
||||
reason be judged legally invalid or ineffective under applicable law, such
|
||||
partial invalidity or ineffectiveness shall not invalidate the remainder
|
||||
of the License, and in such case Affirmer hereby affirms that he or she
|
||||
will not (i) exercise any of his or her remaining Copyright and Related
|
||||
Rights in the Work or (ii) assert any associated claims and causes of
|
||||
action with respect to the Work, in either case contrary to Affirmer's
|
||||
express Statement of Purpose.
|
||||
|
||||
4. Limitations and Disclaimers.
|
||||
|
||||
a. No trademark or patent rights held by Affirmer are waived, abandoned,
|
||||
surrendered, licensed or otherwise affected by this document.
|
||||
b. Affirmer offers the Work as-is and makes no representations or
|
||||
warranties of any kind concerning the Work, express, implied,
|
||||
statutory or otherwise, including without limitation warranties of
|
||||
title, merchantability, fitness for a particular purpose, non
|
||||
infringement, or the absence of latent or other defects, accuracy, or
|
||||
the present or absence of errors, whether or not discoverable, all to
|
||||
the greatest extent permissible under applicable law.
|
||||
c. Affirmer disclaims responsibility for clearing rights of other persons
|
||||
that may apply to the Work or any use thereof, including without
|
||||
limitation any person's Copyright and Related Rights in the Work.
|
||||
Further, Affirmer disclaims responsibility for obtaining any necessary
|
||||
consents, permissions or other rights required for any use of the
|
||||
Work.
|
||||
d. Affirmer understands and acknowledges that Creative Commons is not a
|
||||
party to this document and has no duty or obligation with respect to
|
||||
this CC0 or use of the Work.
|
||||
@@ -162,6 +162,85 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"DataUpdate": {
|
||||
"Scheduler": {
|
||||
"Enable": {
|
||||
"type": "state",
|
||||
"value": true,
|
||||
"option": [
|
||||
true
|
||||
],
|
||||
"option_bold": [
|
||||
true
|
||||
]
|
||||
},
|
||||
"NextRun": {
|
||||
"type": "datetime",
|
||||
"value": "2020-01-01 00:00:00",
|
||||
"validate": "datetime"
|
||||
},
|
||||
"Command": {
|
||||
"type": "input",
|
||||
"value": "DataUpdate",
|
||||
"display": "hide"
|
||||
},
|
||||
"ServerUpdate": {
|
||||
"type": "input",
|
||||
"value": "04:00",
|
||||
"display": "hide"
|
||||
}
|
||||
},
|
||||
"ItemStorage": {
|
||||
"AP": {
|
||||
"type": "stored",
|
||||
"value": {},
|
||||
"display": "hide",
|
||||
"stored": "StoredAP",
|
||||
"order": 1,
|
||||
"color": "#62ea6e"
|
||||
},
|
||||
"Credit": {
|
||||
"type": "stored",
|
||||
"value": {},
|
||||
"display": "hide",
|
||||
"stored": "StoredInt",
|
||||
"order": 2,
|
||||
"color": "#fdec00"
|
||||
},
|
||||
"Pyroxene": {
|
||||
"type": "stored",
|
||||
"value": {},
|
||||
"display": "hide",
|
||||
"stored": "StoredInt",
|
||||
"order": 3,
|
||||
"color": "#21befc"
|
||||
},
|
||||
"BountyTicket": {
|
||||
"type": "stored",
|
||||
"value": {},
|
||||
"display": "hide",
|
||||
"stored": "StoredBountyTicket",
|
||||
"order": 4,
|
||||
"color": "#94cb44"
|
||||
},
|
||||
"ScrimmageTicket": {
|
||||
"type": "stored",
|
||||
"value": {},
|
||||
"display": "hide",
|
||||
"stored": "StoredScrimmageTicket",
|
||||
"order": 5,
|
||||
"color": "#f86c6a"
|
||||
},
|
||||
"TacticalChallengeTicket": {
|
||||
"type": "stored",
|
||||
"value": {},
|
||||
"display": "hide",
|
||||
"stored": "StoredTacticalChallengeTicket",
|
||||
"order": 6,
|
||||
"color": "#7ac8e5"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Cafe": {
|
||||
"Scheduler": {
|
||||
"Enable": {
|
||||
@@ -239,11 +318,11 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"Circle": {
|
||||
"Schedule": {
|
||||
"Scheduler": {
|
||||
"Enable": {
|
||||
"type": "checkbox",
|
||||
"value": true,
|
||||
"value": false,
|
||||
"option": [
|
||||
true,
|
||||
false
|
||||
@@ -256,7 +335,7 @@
|
||||
},
|
||||
"Command": {
|
||||
"type": "input",
|
||||
"value": "Circle",
|
||||
"value": "Schedule",
|
||||
"display": "hide"
|
||||
},
|
||||
"ServerUpdate": {
|
||||
@@ -264,13 +343,138 @@
|
||||
"value": "04:00",
|
||||
"display": "hide"
|
||||
}
|
||||
},
|
||||
"Schedule": {
|
||||
"OnError": {
|
||||
"type": "select",
|
||||
"value": "skip",
|
||||
"option": [
|
||||
"stop",
|
||||
"skip"
|
||||
]
|
||||
}
|
||||
},
|
||||
"Choice1": {
|
||||
"Location": {
|
||||
"type": "select",
|
||||
"value": "None",
|
||||
"option": [
|
||||
"None",
|
||||
0,
|
||||
1,
|
||||
2,
|
||||
3,
|
||||
4,
|
||||
5,
|
||||
6,
|
||||
7,
|
||||
8,
|
||||
9
|
||||
]
|
||||
},
|
||||
"Classrooms": {
|
||||
"type": "textarea",
|
||||
"value": null
|
||||
}
|
||||
},
|
||||
"Choice2": {
|
||||
"Location": {
|
||||
"type": "select",
|
||||
"value": "None",
|
||||
"option": [
|
||||
"None",
|
||||
0,
|
||||
1,
|
||||
2,
|
||||
3,
|
||||
4,
|
||||
5,
|
||||
6,
|
||||
7,
|
||||
8,
|
||||
9
|
||||
]
|
||||
},
|
||||
"Classrooms": {
|
||||
"type": "textarea",
|
||||
"value": null
|
||||
}
|
||||
},
|
||||
"Choice3": {
|
||||
"Location": {
|
||||
"type": "select",
|
||||
"value": "None",
|
||||
"option": [
|
||||
"None",
|
||||
0,
|
||||
1,
|
||||
2,
|
||||
3,
|
||||
4,
|
||||
5,
|
||||
6,
|
||||
7,
|
||||
8,
|
||||
9
|
||||
]
|
||||
},
|
||||
"Classrooms": {
|
||||
"type": "textarea",
|
||||
"value": null
|
||||
}
|
||||
},
|
||||
"Choice4": {
|
||||
"Location": {
|
||||
"type": "select",
|
||||
"value": "None",
|
||||
"option": [
|
||||
"None",
|
||||
0,
|
||||
1,
|
||||
2,
|
||||
3,
|
||||
4,
|
||||
5,
|
||||
6,
|
||||
7,
|
||||
8,
|
||||
9
|
||||
]
|
||||
},
|
||||
"Classrooms": {
|
||||
"type": "textarea",
|
||||
"value": null
|
||||
}
|
||||
},
|
||||
"Choice5": {
|
||||
"Location": {
|
||||
"type": "select",
|
||||
"value": "None",
|
||||
"option": [
|
||||
"None",
|
||||
0,
|
||||
1,
|
||||
2,
|
||||
3,
|
||||
4,
|
||||
5,
|
||||
6,
|
||||
7,
|
||||
8,
|
||||
9
|
||||
]
|
||||
},
|
||||
"Classrooms": {
|
||||
"type": "textarea",
|
||||
"value": null
|
||||
}
|
||||
}
|
||||
},
|
||||
"Mail": {
|
||||
"Shop": {
|
||||
"Scheduler": {
|
||||
"Enable": {
|
||||
"type": "checkbox",
|
||||
"value": true,
|
||||
"value": false,
|
||||
"option": [
|
||||
true,
|
||||
false
|
||||
@@ -283,7 +487,7 @@
|
||||
},
|
||||
"Command": {
|
||||
"type": "input",
|
||||
"value": "Mail",
|
||||
"value": "Shop",
|
||||
"display": "hide"
|
||||
},
|
||||
"ServerUpdate": {
|
||||
@@ -291,6 +495,178 @@
|
||||
"value": "04:00",
|
||||
"display": "hide"
|
||||
}
|
||||
},
|
||||
"NormalShop": {
|
||||
"Enable": {
|
||||
"type": "checkbox",
|
||||
"value": false
|
||||
},
|
||||
"Purchases": {
|
||||
"type": "select",
|
||||
"value": 1,
|
||||
"option": [
|
||||
1,
|
||||
2,
|
||||
3,
|
||||
4
|
||||
]
|
||||
},
|
||||
"1": {
|
||||
"type": "checkbox",
|
||||
"value": false
|
||||
},
|
||||
"2": {
|
||||
"type": "checkbox",
|
||||
"value": false
|
||||
},
|
||||
"3": {
|
||||
"type": "checkbox",
|
||||
"value": false
|
||||
},
|
||||
"4": {
|
||||
"type": "checkbox",
|
||||
"value": false
|
||||
},
|
||||
"5": {
|
||||
"type": "checkbox",
|
||||
"value": false
|
||||
},
|
||||
"6": {
|
||||
"type": "checkbox",
|
||||
"value": false
|
||||
},
|
||||
"7": {
|
||||
"type": "checkbox",
|
||||
"value": false
|
||||
},
|
||||
"8": {
|
||||
"type": "checkbox",
|
||||
"value": false
|
||||
},
|
||||
"9": {
|
||||
"type": "checkbox",
|
||||
"value": false
|
||||
},
|
||||
"10": {
|
||||
"type": "checkbox",
|
||||
"value": false
|
||||
},
|
||||
"11": {
|
||||
"type": "checkbox",
|
||||
"value": false
|
||||
},
|
||||
"12": {
|
||||
"type": "checkbox",
|
||||
"value": false
|
||||
},
|
||||
"13": {
|
||||
"type": "checkbox",
|
||||
"value": false
|
||||
},
|
||||
"14": {
|
||||
"type": "checkbox",
|
||||
"value": false
|
||||
},
|
||||
"15": {
|
||||
"type": "checkbox",
|
||||
"value": false
|
||||
},
|
||||
"16": {
|
||||
"type": "checkbox",
|
||||
"value": false
|
||||
},
|
||||
"17": {
|
||||
"type": "checkbox",
|
||||
"value": false
|
||||
},
|
||||
"18": {
|
||||
"type": "checkbox",
|
||||
"value": false
|
||||
},
|
||||
"19": {
|
||||
"type": "checkbox",
|
||||
"value": false
|
||||
},
|
||||
"20": {
|
||||
"type": "checkbox",
|
||||
"value": false
|
||||
}
|
||||
},
|
||||
"TacticalChallengeShop": {
|
||||
"Enable": {
|
||||
"type": "checkbox",
|
||||
"value": false
|
||||
},
|
||||
"Purchases": {
|
||||
"type": "select",
|
||||
"value": 1,
|
||||
"option": [
|
||||
1,
|
||||
2,
|
||||
3,
|
||||
4
|
||||
]
|
||||
},
|
||||
"1": {
|
||||
"type": "checkbox",
|
||||
"value": false
|
||||
},
|
||||
"2": {
|
||||
"type": "checkbox",
|
||||
"value": false
|
||||
},
|
||||
"3": {
|
||||
"type": "checkbox",
|
||||
"value": false
|
||||
},
|
||||
"4": {
|
||||
"type": "checkbox",
|
||||
"value": false
|
||||
},
|
||||
"5": {
|
||||
"type": "checkbox",
|
||||
"value": false
|
||||
},
|
||||
"6": {
|
||||
"type": "checkbox",
|
||||
"value": false
|
||||
},
|
||||
"7": {
|
||||
"type": "checkbox",
|
||||
"value": false
|
||||
},
|
||||
"8": {
|
||||
"type": "checkbox",
|
||||
"value": false
|
||||
},
|
||||
"9": {
|
||||
"type": "checkbox",
|
||||
"value": false
|
||||
},
|
||||
"10": {
|
||||
"type": "checkbox",
|
||||
"value": false
|
||||
},
|
||||
"11": {
|
||||
"type": "checkbox",
|
||||
"value": false
|
||||
},
|
||||
"12": {
|
||||
"type": "checkbox",
|
||||
"value": false
|
||||
},
|
||||
"13": {
|
||||
"type": "checkbox",
|
||||
"value": false
|
||||
},
|
||||
"14": {
|
||||
"type": "checkbox",
|
||||
"value": false
|
||||
},
|
||||
"15": {
|
||||
"type": "checkbox",
|
||||
"value": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"Bounty": {
|
||||
@@ -506,7 +882,7 @@
|
||||
},
|
||||
"ServerUpdate": {
|
||||
"type": "input",
|
||||
"value": "15:00",
|
||||
"value": "14:00",
|
||||
"display": "hide"
|
||||
}
|
||||
},
|
||||
@@ -523,16 +899,14 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"DataUpdate": {
|
||||
"Mission": {
|
||||
"Scheduler": {
|
||||
"Enable": {
|
||||
"type": "state",
|
||||
"value": true,
|
||||
"type": "checkbox",
|
||||
"value": false,
|
||||
"option": [
|
||||
true
|
||||
],
|
||||
"option_bold": [
|
||||
true
|
||||
true,
|
||||
false
|
||||
]
|
||||
},
|
||||
"NextRun": {
|
||||
@@ -542,7 +916,7 @@
|
||||
},
|
||||
"Command": {
|
||||
"type": "input",
|
||||
"value": "DataUpdate",
|
||||
"value": "Mission",
|
||||
"display": "hide"
|
||||
},
|
||||
"ServerUpdate": {
|
||||
@@ -550,55 +924,113 @@
|
||||
"value": "04:00",
|
||||
"display": "hide"
|
||||
}
|
||||
},
|
||||
"ItemStorage": {
|
||||
"AP": {
|
||||
"type": "stored",
|
||||
"value": {},
|
||||
"display": "hide",
|
||||
"stored": "StoredAP",
|
||||
"order": 1,
|
||||
"color": "#62ea6e"
|
||||
}
|
||||
},
|
||||
"Circle": {
|
||||
"Scheduler": {
|
||||
"Enable": {
|
||||
"type": "checkbox",
|
||||
"value": true,
|
||||
"option": [
|
||||
true,
|
||||
false
|
||||
]
|
||||
},
|
||||
"Credit": {
|
||||
"type": "stored",
|
||||
"value": {},
|
||||
"display": "hide",
|
||||
"stored": "StoredInt",
|
||||
"order": 2,
|
||||
"color": "#fdec00"
|
||||
"NextRun": {
|
||||
"type": "datetime",
|
||||
"value": "2020-01-01 00:00:00",
|
||||
"validate": "datetime"
|
||||
},
|
||||
"Pyroxene": {
|
||||
"type": "stored",
|
||||
"value": {},
|
||||
"display": "hide",
|
||||
"stored": "StoredInt",
|
||||
"order": 3,
|
||||
"color": "#21befc"
|
||||
"Command": {
|
||||
"type": "input",
|
||||
"value": "Circle",
|
||||
"display": "hide"
|
||||
},
|
||||
"BountyTicket": {
|
||||
"type": "stored",
|
||||
"value": {},
|
||||
"display": "hide",
|
||||
"stored": "StoredBountyTicket",
|
||||
"order": 4,
|
||||
"color": "#94cb44"
|
||||
"ServerUpdate": {
|
||||
"type": "input",
|
||||
"value": "04:00",
|
||||
"display": "hide"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Task": {
|
||||
"Scheduler": {
|
||||
"Enable": {
|
||||
"type": "checkbox",
|
||||
"value": false,
|
||||
"option": [
|
||||
true,
|
||||
false
|
||||
]
|
||||
},
|
||||
"ScrimmageTicket": {
|
||||
"type": "stored",
|
||||
"value": {},
|
||||
"display": "hide",
|
||||
"stored": "StoredScrimmageTicket",
|
||||
"order": 5,
|
||||
"color": "#f86c6a"
|
||||
"NextRun": {
|
||||
"type": "datetime",
|
||||
"value": "2020-01-01 00:00:00",
|
||||
"validate": "datetime"
|
||||
},
|
||||
"TacticalChallengeTicket": {
|
||||
"type": "stored",
|
||||
"value": {},
|
||||
"display": "hide",
|
||||
"stored": "StoredTacticalChallengeTicket",
|
||||
"order": 6,
|
||||
"color": "#7ac8e5"
|
||||
"Command": {
|
||||
"type": "input",
|
||||
"value": "Task",
|
||||
"display": "hide"
|
||||
},
|
||||
"ServerUpdate": {
|
||||
"type": "input",
|
||||
"value": "04:00",
|
||||
"display": "hide"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Mail": {
|
||||
"Scheduler": {
|
||||
"Enable": {
|
||||
"type": "checkbox",
|
||||
"value": true,
|
||||
"option": [
|
||||
true,
|
||||
false
|
||||
]
|
||||
},
|
||||
"NextRun": {
|
||||
"type": "datetime",
|
||||
"value": "2020-01-01 00:00:00",
|
||||
"validate": "datetime"
|
||||
},
|
||||
"Command": {
|
||||
"type": "input",
|
||||
"value": "Mail",
|
||||
"display": "hide"
|
||||
},
|
||||
"ServerUpdate": {
|
||||
"type": "input",
|
||||
"value": "04:00",
|
||||
"display": "hide"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Momotalk": {
|
||||
"Scheduler": {
|
||||
"Enable": {
|
||||
"type": "checkbox",
|
||||
"value": false,
|
||||
"option": [
|
||||
true,
|
||||
false
|
||||
]
|
||||
},
|
||||
"NextRun": {
|
||||
"type": "datetime",
|
||||
"value": "2020-01-01 00:00:00",
|
||||
"validate": "datetime"
|
||||
},
|
||||
"Command": {
|
||||
"type": "input",
|
||||
"value": "Momotalk",
|
||||
"display": "hide"
|
||||
},
|
||||
"ServerUpdate": {
|
||||
"type": "input",
|
||||
"value": "04:00",
|
||||
"display": "hide"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -93,6 +93,47 @@ Invitation:
|
||||
type: textarea
|
||||
Substitute: false
|
||||
|
||||
Schedule:
|
||||
OnError:
|
||||
value: skip
|
||||
option: [ stop, skip ]
|
||||
|
||||
Choice1:
|
||||
Location:
|
||||
value: None
|
||||
option: [ None, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ]
|
||||
Classrooms:
|
||||
value: null
|
||||
type: textarea
|
||||
Choice2:
|
||||
Location:
|
||||
value: None
|
||||
option: [ None, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ]
|
||||
Classrooms:
|
||||
value: null
|
||||
type: textarea
|
||||
Choice3:
|
||||
Location:
|
||||
value: None
|
||||
option: [ None, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ]
|
||||
Classrooms:
|
||||
value: null
|
||||
type: textarea
|
||||
Choice4:
|
||||
Location:
|
||||
value: None
|
||||
option: [ None, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ]
|
||||
Classrooms:
|
||||
value: null
|
||||
type: textarea
|
||||
Choice5:
|
||||
Location:
|
||||
value: None
|
||||
option: [ None, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ]
|
||||
Classrooms:
|
||||
value: null
|
||||
type: textarea
|
||||
|
||||
Bounty:
|
||||
OnError:
|
||||
value: skip
|
||||
@@ -140,6 +181,53 @@ TacticalChallenge:
|
||||
value: 0
|
||||
option: [ 0, 1, 2, 3 ]
|
||||
|
||||
NormalShop:
|
||||
Enable: false
|
||||
Purchases:
|
||||
value: 1
|
||||
option: [ 1, 2, 3, 4 ]
|
||||
"1": false
|
||||
"2": false
|
||||
"3": false
|
||||
"4": false
|
||||
"5": false
|
||||
"6": false
|
||||
"7": false
|
||||
"8": false
|
||||
"9": false
|
||||
"10": false
|
||||
"11": false
|
||||
"12": false
|
||||
"13": false
|
||||
"14": false
|
||||
"15": false
|
||||
"16": false
|
||||
"17": false
|
||||
"18": false
|
||||
"19": false
|
||||
"20": false
|
||||
|
||||
TacticalChallengeShop:
|
||||
Enable: false
|
||||
Purchases:
|
||||
value: 1
|
||||
option: [ 1, 2, 3, 4 ]
|
||||
"1": false
|
||||
"2": false
|
||||
"3": false
|
||||
"4": false
|
||||
"5": false
|
||||
"6": false
|
||||
"7": false
|
||||
"8": false
|
||||
"9": false
|
||||
"10": false
|
||||
"11": false
|
||||
"12": false
|
||||
"13": false
|
||||
"14": false
|
||||
"15": false
|
||||
|
||||
ItemStorage:
|
||||
AP:
|
||||
stored: StoredAP
|
||||
|
||||
@@ -4,20 +4,37 @@
|
||||
"page": "setting",
|
||||
"tasks": [
|
||||
"Alas",
|
||||
"Restart"
|
||||
"Restart",
|
||||
"DataUpdate"
|
||||
]
|
||||
},
|
||||
"Daily": {
|
||||
"menu": "list",
|
||||
"menu": "collapse",
|
||||
"page": "setting",
|
||||
"tasks": [
|
||||
"Cafe",
|
||||
"Circle",
|
||||
"Mail",
|
||||
"Schedule",
|
||||
"Shop"
|
||||
]
|
||||
},
|
||||
"Farm": {
|
||||
"menu": "collapse",
|
||||
"page": "setting",
|
||||
"tasks": [
|
||||
"Bounty",
|
||||
"Scrimmage",
|
||||
"TacticalChallenge",
|
||||
"DataUpdate"
|
||||
"Mission"
|
||||
]
|
||||
},
|
||||
"Reward": {
|
||||
"menu": "collapse",
|
||||
"page": "setting",
|
||||
"tasks": [
|
||||
"Circle",
|
||||
"Task",
|
||||
"Mail",
|
||||
"Momotalk"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -29,7 +29,7 @@ Cafe:
|
||||
|
||||
TacticalChallenge:
|
||||
Scheduler:
|
||||
ServerUpdate: "15:00"
|
||||
ServerUpdate: "14:00"
|
||||
|
||||
DataUpdate:
|
||||
Scheduler:
|
||||
|
||||
@@ -15,21 +15,39 @@ Alas:
|
||||
- Optimization
|
||||
Restart:
|
||||
- Scheduler
|
||||
DataUpdate:
|
||||
- Scheduler
|
||||
- ItemStorage
|
||||
|
||||
# ==================== Daily ====================
|
||||
|
||||
Daily:
|
||||
menu: 'list'
|
||||
menu: 'collapse'
|
||||
page: 'setting'
|
||||
tasks:
|
||||
Cafe:
|
||||
- Scheduler
|
||||
- Cafe
|
||||
- Invitation
|
||||
Circle:
|
||||
Schedule:
|
||||
- Scheduler
|
||||
Mail:
|
||||
- Schedule
|
||||
- Choice1
|
||||
- Choice2
|
||||
- Choice3
|
||||
- Choice4
|
||||
- Choice5
|
||||
Shop:
|
||||
- Scheduler
|
||||
- NormalShop
|
||||
- TacticalChallengeShop
|
||||
|
||||
# ==================== Farm ====================
|
||||
|
||||
Farm:
|
||||
menu: 'collapse'
|
||||
page: 'setting'
|
||||
tasks:
|
||||
Bounty:
|
||||
- Scheduler
|
||||
- Bounty
|
||||
@@ -45,6 +63,20 @@ Daily:
|
||||
TacticalChallenge:
|
||||
- Scheduler
|
||||
- TacticalChallenge
|
||||
DataUpdate:
|
||||
Mission:
|
||||
- Scheduler
|
||||
- ItemStorage
|
||||
|
||||
# ==================== Rewards ====================
|
||||
|
||||
Reward:
|
||||
menu: 'collapse'
|
||||
page: 'setting'
|
||||
tasks:
|
||||
Circle:
|
||||
- Scheduler
|
||||
Task:
|
||||
- Scheduler
|
||||
Mail:
|
||||
- Scheduler
|
||||
Momotalk:
|
||||
- Scheduler
|
||||
@@ -52,6 +52,29 @@ class GeneratedConfig:
|
||||
Invitation_Name = None
|
||||
Invitation_Substitute = False
|
||||
|
||||
# Group `Schedule`
|
||||
Schedule_OnError = 'skip' # stop, skip
|
||||
|
||||
# Group `Choice1`
|
||||
Choice1_Location = 'None' # None, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9
|
||||
Choice1_Classrooms = None
|
||||
|
||||
# Group `Choice2`
|
||||
Choice2_Location = 'None' # None, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9
|
||||
Choice2_Classrooms = None
|
||||
|
||||
# Group `Choice3`
|
||||
Choice3_Location = 'None' # None, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9
|
||||
Choice3_Classrooms = None
|
||||
|
||||
# Group `Choice4`
|
||||
Choice4_Location = 'None' # None, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9
|
||||
Choice4_Classrooms = None
|
||||
|
||||
# Group `Choice5`
|
||||
Choice5_Location = 'None' # None, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9
|
||||
Choice5_Classrooms = None
|
||||
|
||||
# Group `Bounty`
|
||||
Bounty_OnError = 'skip' # stop, skip
|
||||
|
||||
@@ -85,6 +108,49 @@ class GeneratedConfig:
|
||||
# Group `TacticalChallenge`
|
||||
TacticalChallenge_PlayerSelect = 0 # 0, 1, 2, 3
|
||||
|
||||
# Group `NormalShop`
|
||||
NormalShop_Enable = False
|
||||
NormalShop_Purchases = 1 # 1, 2, 3, 4
|
||||
NormalShop_1 = False
|
||||
NormalShop_2 = False
|
||||
NormalShop_3 = False
|
||||
NormalShop_4 = False
|
||||
NormalShop_5 = False
|
||||
NormalShop_6 = False
|
||||
NormalShop_7 = False
|
||||
NormalShop_8 = False
|
||||
NormalShop_9 = False
|
||||
NormalShop_10 = False
|
||||
NormalShop_11 = False
|
||||
NormalShop_12 = False
|
||||
NormalShop_13 = False
|
||||
NormalShop_14 = False
|
||||
NormalShop_15 = False
|
||||
NormalShop_16 = False
|
||||
NormalShop_17 = False
|
||||
NormalShop_18 = False
|
||||
NormalShop_19 = False
|
||||
NormalShop_20 = False
|
||||
|
||||
# Group `TacticalChallengeShop`
|
||||
TacticalChallengeShop_Enable = False
|
||||
TacticalChallengeShop_Purchases = 1 # 1, 2, 3, 4
|
||||
TacticalChallengeShop_1 = False
|
||||
TacticalChallengeShop_2 = False
|
||||
TacticalChallengeShop_3 = False
|
||||
TacticalChallengeShop_4 = False
|
||||
TacticalChallengeShop_5 = False
|
||||
TacticalChallengeShop_6 = False
|
||||
TacticalChallengeShop_7 = False
|
||||
TacticalChallengeShop_8 = False
|
||||
TacticalChallengeShop_9 = False
|
||||
TacticalChallengeShop_10 = False
|
||||
TacticalChallengeShop_11 = False
|
||||
TacticalChallengeShop_12 = False
|
||||
TacticalChallengeShop_13 = False
|
||||
TacticalChallengeShop_14 = False
|
||||
TacticalChallengeShop_15 = False
|
||||
|
||||
# Group `ItemStorage`
|
||||
ItemStorage_AP = {}
|
||||
ItemStorage_Credit = {}
|
||||
|
||||
@@ -8,8 +8,8 @@ class ManualConfig:
|
||||
|
||||
SCHEDULER_PRIORITY = """
|
||||
Restart
|
||||
> Cafe > Circle > Mail > DataUpdate > Bounty
|
||||
> Scrimmage > TacticalChallenge
|
||||
> Cafe > TacticalChallenge > Circle > Mail
|
||||
> DataUpdate > Bounty > Scrimmage > Schedule > Task > Shop > Mission > Momotalk
|
||||
"""
|
||||
|
||||
"""
|
||||
|
||||
@@ -7,6 +7,14 @@
|
||||
"Daily": {
|
||||
"name": "Daily",
|
||||
"help": ""
|
||||
},
|
||||
"Farm": {
|
||||
"name": "Farm",
|
||||
"help": ""
|
||||
},
|
||||
"Reward": {
|
||||
"name": "Reward",
|
||||
"help": ""
|
||||
}
|
||||
},
|
||||
"Task": {
|
||||
@@ -18,16 +26,20 @@
|
||||
"name": "Error Handling",
|
||||
"help": ""
|
||||
},
|
||||
"DataUpdate": {
|
||||
"name": "Dashboard Upd",
|
||||
"help": ""
|
||||
},
|
||||
"Cafe": {
|
||||
"name": "Cafe",
|
||||
"help": ""
|
||||
},
|
||||
"Circle": {
|
||||
"name": "Club",
|
||||
"help": ""
|
||||
"Schedule": {
|
||||
"name": "Lesson",
|
||||
"help": "AAS will execute Lesson starting from Choice 1 to Choice 5.\nIt will ignore any Choice that have Location set as None or the text area for classrooms is empty.\nIf any of the active Choices have incorrect input, it will perform the action set in Error handling."
|
||||
},
|
||||
"Mail": {
|
||||
"name": "Mailbox",
|
||||
"Shop": {
|
||||
"name": "Shop",
|
||||
"help": ""
|
||||
},
|
||||
"Bounty": {
|
||||
@@ -42,8 +54,24 @@
|
||||
"name": "Tactical Challenge",
|
||||
"help": ""
|
||||
},
|
||||
"DataUpdate": {
|
||||
"name": "Dashboard Upd",
|
||||
"Mission": {
|
||||
"name": "Mission/Commissions/Event",
|
||||
"help": "Open MCE Manager for additional settings. Must be opened if it's your first time!"
|
||||
},
|
||||
"Circle": {
|
||||
"name": "Club",
|
||||
"help": ""
|
||||
},
|
||||
"Task": {
|
||||
"name": "Tasks",
|
||||
"help": ""
|
||||
},
|
||||
"Mail": {
|
||||
"name": "Mailbox",
|
||||
"help": ""
|
||||
},
|
||||
"Momotalk": {
|
||||
"name": "MomoTalk",
|
||||
"help": ""
|
||||
}
|
||||
},
|
||||
@@ -253,6 +281,143 @@
|
||||
"help": "Whether to replace the existing student with their alt.\nIf not, try to match the next student"
|
||||
}
|
||||
},
|
||||
"Schedule": {
|
||||
"_info": {
|
||||
"name": "Lesson Settings",
|
||||
"help": ""
|
||||
},
|
||||
"OnError": {
|
||||
"name": "Error Handling",
|
||||
"help": "Perform the selected action when an error occurs (ticket not enough or any invalid setting)",
|
||||
"stop": "Stop script",
|
||||
"skip": "Skip current task"
|
||||
}
|
||||
},
|
||||
"Choice1": {
|
||||
"_info": {
|
||||
"name": "Choice 1",
|
||||
"help": ""
|
||||
},
|
||||
"Location": {
|
||||
"name": "Location",
|
||||
"help": "",
|
||||
"None": "None",
|
||||
"0": "Schale Office",
|
||||
"1": "Schale Residence",
|
||||
"2": "Gehenna",
|
||||
"3": "Abydos",
|
||||
"4": "Millennium",
|
||||
"5": "Trinity",
|
||||
"6": "Red Winter",
|
||||
"7": "Hyakkiyako",
|
||||
"8": "D.U. Shiratori",
|
||||
"9": "Shanhaijing"
|
||||
},
|
||||
"Classrooms": {
|
||||
"name": "Classrooms",
|
||||
"help": "Type a number from 1 to 9 that represents the classroom position in the locations popup.\nUse > to connect multiple classrooms and AAS will select them following the order they appear. Example:\n5 > 3 > 2 > 4 > 1"
|
||||
}
|
||||
},
|
||||
"Choice2": {
|
||||
"_info": {
|
||||
"name": "Choice 2",
|
||||
"help": ""
|
||||
},
|
||||
"Location": {
|
||||
"name": "Location",
|
||||
"help": "",
|
||||
"None": "None",
|
||||
"0": "Schale Office",
|
||||
"1": "Schale Residence",
|
||||
"2": "Gehenna",
|
||||
"3": "Abydos",
|
||||
"4": "Millennium",
|
||||
"5": "Trinity",
|
||||
"6": "Red Winter",
|
||||
"7": "Hyakkiyako",
|
||||
"8": "D.U. Shiratori",
|
||||
"9": "Shanhaijing"
|
||||
},
|
||||
"Classrooms": {
|
||||
"name": "Classrooms",
|
||||
"help": ""
|
||||
}
|
||||
},
|
||||
"Choice3": {
|
||||
"_info": {
|
||||
"name": "Choice 3",
|
||||
"help": ""
|
||||
},
|
||||
"Location": {
|
||||
"name": "Location",
|
||||
"help": "",
|
||||
"None": "None",
|
||||
"0": "Schale Office",
|
||||
"1": "Schale Residence",
|
||||
"2": "Gehenna",
|
||||
"3": "Abydos",
|
||||
"4": "Millennium",
|
||||
"5": "Trinity",
|
||||
"6": "Red Winter",
|
||||
"7": "Hyakkiyako",
|
||||
"8": "D.U. Shiratori",
|
||||
"9": "Shanhaijing"
|
||||
},
|
||||
"Classrooms": {
|
||||
"name": "Classrooms",
|
||||
"help": ""
|
||||
}
|
||||
},
|
||||
"Choice4": {
|
||||
"_info": {
|
||||
"name": "Choice 4",
|
||||
"help": ""
|
||||
},
|
||||
"Location": {
|
||||
"name": "Location",
|
||||
"help": "",
|
||||
"None": "None",
|
||||
"0": "Schale Office",
|
||||
"1": "Schale Residence",
|
||||
"2": "Gehenna",
|
||||
"3": "Abydos",
|
||||
"4": "Millennium",
|
||||
"5": "Trinity",
|
||||
"6": "Red Winter",
|
||||
"7": "Hyakkiyako",
|
||||
"8": "D.U. Shiratori",
|
||||
"9": "Shanhaijing"
|
||||
},
|
||||
"Classrooms": {
|
||||
"name": "Classrooms",
|
||||
"help": ""
|
||||
}
|
||||
},
|
||||
"Choice5": {
|
||||
"_info": {
|
||||
"name": "Choice 5",
|
||||
"help": ""
|
||||
},
|
||||
"Location": {
|
||||
"name": "Location",
|
||||
"help": "",
|
||||
"None": "None",
|
||||
"0": "Schale Office",
|
||||
"1": "Schale Residence",
|
||||
"2": "Gehenna",
|
||||
"3": "Abydos",
|
||||
"4": "Millennium",
|
||||
"5": "Trinity",
|
||||
"6": "Red Winter",
|
||||
"7": "Hyakkiyako",
|
||||
"8": "D.U. Shiratori",
|
||||
"9": "Shanhaijing"
|
||||
},
|
||||
"Classrooms": {
|
||||
"name": "Classrooms",
|
||||
"help": ""
|
||||
}
|
||||
},
|
||||
"Bounty": {
|
||||
"_info": {
|
||||
"name": "Bounty Settings",
|
||||
@@ -420,6 +585,182 @@
|
||||
"3": "Third"
|
||||
}
|
||||
},
|
||||
"NormalShop": {
|
||||
"_info": {
|
||||
"name": "Normal Shop Settings",
|
||||
"help": ""
|
||||
},
|
||||
"Enable": {
|
||||
"name": "Enable",
|
||||
"help": ""
|
||||
},
|
||||
"Purchases": {
|
||||
"name": "Number of Purchases",
|
||||
"help": "Default can be purchased once + number of refreshes = number of purchases, for example, 2 purchases = 1 default purchase + 1 refresh purchase",
|
||||
"1": "1",
|
||||
"2": "2",
|
||||
"3": "3",
|
||||
"4": "4"
|
||||
},
|
||||
"1": {
|
||||
"name": "1",
|
||||
"help": "x5 Novice Activity Report - 12,500 Credits"
|
||||
},
|
||||
"2": {
|
||||
"name": "2",
|
||||
"help": "x5 Normal Activity Report - 125,000 Credits"
|
||||
},
|
||||
"3": {
|
||||
"name": "3",
|
||||
"help": "x3 Advanced Activity Report - 300,000 Credits"
|
||||
},
|
||||
"4": {
|
||||
"name": "4",
|
||||
"help": "x1 Superior Activity Report - 500,000 Credits"
|
||||
},
|
||||
"5": {
|
||||
"name": "5",
|
||||
"help": "x5 Lesser Enhancement Stone - 10,000 Credits"
|
||||
},
|
||||
"6": {
|
||||
"name": "6",
|
||||
"help": "x5 Normal Enhancement Stone - 40,000 Credits"
|
||||
},
|
||||
"7": {
|
||||
"name": "7",
|
||||
"help": "x3 Advanced Enhancement Stone - 96,000 Credits"
|
||||
},
|
||||
"8": {
|
||||
"name": "8",
|
||||
"help": "x1 Superior Enhancement Stone - 128,000 Credits"
|
||||
},
|
||||
"9": {
|
||||
"name": "9",
|
||||
"help": "x5 Lesser Enhancement Stone - 10,000 Credits"
|
||||
},
|
||||
"10": {
|
||||
"name": "10",
|
||||
"help": "x5 Normal Enhancement Stone - 40,000 Credits"
|
||||
},
|
||||
"11": {
|
||||
"name": "11",
|
||||
"help": "x3 Advanced Enhancement Stone - 96,000 Credits"
|
||||
},
|
||||
"12": {
|
||||
"name": "12",
|
||||
"help": "x1 Superior Enhancement Stone - 128,000 Credits"
|
||||
},
|
||||
"13": {
|
||||
"name": "13",
|
||||
"help": "x10 Lesser Enhancement Stone - 20,000 Credits"
|
||||
},
|
||||
"14": {
|
||||
"name": "14",
|
||||
"help": "x10 Normal Enhancement Stone - 80,000 Credits"
|
||||
},
|
||||
"15": {
|
||||
"name": "15",
|
||||
"help": "x6 Advanced Enhancement Stone - 192,000 Credits"
|
||||
},
|
||||
"16": {
|
||||
"name": "16",
|
||||
"help": "x2 Superior Enhancement Stone - 256,000 Credits"
|
||||
},
|
||||
"17": {
|
||||
"name": "17",
|
||||
"help": "x1 Random Selection - 8,000 Credits"
|
||||
},
|
||||
"18": {
|
||||
"name": "18",
|
||||
"help": "x1 Random Selection - 8,000 Credits"
|
||||
},
|
||||
"19": {
|
||||
"name": "19",
|
||||
"help": "x1 Random Selection - 25,000 Credits"
|
||||
},
|
||||
"20": {
|
||||
"name": "20",
|
||||
"help": "x1 Random Selection - 25,000 Credits"
|
||||
}
|
||||
},
|
||||
"TacticalChallengeShop": {
|
||||
"_info": {
|
||||
"name": "Tactical Challenge Shop Settings",
|
||||
"help": ""
|
||||
},
|
||||
"Enable": {
|
||||
"name": "Enable",
|
||||
"help": ""
|
||||
},
|
||||
"Purchases": {
|
||||
"name": "Number of Purchases",
|
||||
"help": "",
|
||||
"1": "1",
|
||||
"2": "2",
|
||||
"3": "3",
|
||||
"4": "4"
|
||||
},
|
||||
"1": {
|
||||
"name": "1",
|
||||
"help": "x5 Shizuko's Eleph - 50 Coins"
|
||||
},
|
||||
"2": {
|
||||
"name": "2",
|
||||
"help": "x5 Mashiro's Eleph - 50 Coins"
|
||||
},
|
||||
"3": {
|
||||
"name": "3",
|
||||
"help": "x5 Saya's Eleph - 50 Coins"
|
||||
},
|
||||
"4": {
|
||||
"name": "4",
|
||||
"help": "x5 Fuuka's Eleph - 50 Coins"
|
||||
},
|
||||
"5": {
|
||||
"name": "5",
|
||||
"help": "x5 Utaha's Eleph - 50 Coins"
|
||||
},
|
||||
"6": {
|
||||
"name": "6",
|
||||
"help": "x1 Lesser Drink(30 AP) - 15 Coins"
|
||||
},
|
||||
"7": {
|
||||
"name": "7",
|
||||
"help": "x1 Normal Drink(60 AP) - 30 Coins"
|
||||
},
|
||||
"8": {
|
||||
"name": "8",
|
||||
"help": "x10 Novice Activity Report - 5 Coins"
|
||||
},
|
||||
"9": {
|
||||
"name": "9",
|
||||
"help": "x5 Normal Activity Report - 25 Coins"
|
||||
},
|
||||
"10": {
|
||||
"name": "10",
|
||||
"help": "x3 Advanced Activity Report - 60 Coins"
|
||||
},
|
||||
"11": {
|
||||
"name": "11",
|
||||
"help": "x1 Superior Activity Report - 100 Coins"
|
||||
},
|
||||
"12": {
|
||||
"name": "12",
|
||||
"help": "x5000 Credits - 4 Coins"
|
||||
},
|
||||
"13": {
|
||||
"name": "13",
|
||||
"help": "x25k Credits - 20 Coins"
|
||||
},
|
||||
"14": {
|
||||
"name": "14",
|
||||
"help": "x75k Credits - 60 Coins"
|
||||
},
|
||||
"15": {
|
||||
"name": "15",
|
||||
"help": "x125k Credits - 100 Coins"
|
||||
}
|
||||
},
|
||||
"ItemStorage": {
|
||||
"_info": {
|
||||
"name": "ItemStorage._info.name",
|
||||
|
||||
@@ -7,6 +7,14 @@
|
||||
"Daily": {
|
||||
"name": "每日",
|
||||
"help": ""
|
||||
},
|
||||
"Farm": {
|
||||
"name": "扫荡",
|
||||
"help": ""
|
||||
},
|
||||
"Reward": {
|
||||
"name": "收菜",
|
||||
"help": ""
|
||||
}
|
||||
},
|
||||
"Task": {
|
||||
@@ -18,16 +26,20 @@
|
||||
"name": "异常处理",
|
||||
"help": ""
|
||||
},
|
||||
"DataUpdate": {
|
||||
"name": "仪表盘更新",
|
||||
"help": ""
|
||||
},
|
||||
"Cafe": {
|
||||
"name": "咖啡厅",
|
||||
"help": ""
|
||||
},
|
||||
"Circle": {
|
||||
"name": "公会",
|
||||
"help": "社团 / 小组"
|
||||
"Schedule": {
|
||||
"name": "Task.Schedule.name",
|
||||
"help": "Task.Schedule.help"
|
||||
},
|
||||
"Mail": {
|
||||
"name": "邮箱",
|
||||
"Shop": {
|
||||
"name": "商店",
|
||||
"help": ""
|
||||
},
|
||||
"Bounty": {
|
||||
@@ -42,8 +54,24 @@
|
||||
"name": "战术对抗赛",
|
||||
"help": "战术大赛 / 竞技场"
|
||||
},
|
||||
"DataUpdate": {
|
||||
"name": "仪表盘更新",
|
||||
"Mission": {
|
||||
"name": "Task.Mission.name",
|
||||
"help": "Task.Mission.help"
|
||||
},
|
||||
"Circle": {
|
||||
"name": "公会",
|
||||
"help": "社团 / 小组"
|
||||
},
|
||||
"Task": {
|
||||
"name": "任务",
|
||||
"help": ""
|
||||
},
|
||||
"Mail": {
|
||||
"name": "邮箱",
|
||||
"help": ""
|
||||
},
|
||||
"Momotalk": {
|
||||
"name": "Momotalk",
|
||||
"help": ""
|
||||
}
|
||||
},
|
||||
@@ -253,6 +281,143 @@
|
||||
"help": "若咖啡厅已存在所邀请学生的不同服装,选择是否替换该学生\n若不替换,则尝试匹配下一位学生"
|
||||
}
|
||||
},
|
||||
"Schedule": {
|
||||
"_info": {
|
||||
"name": "Schedule._info.name",
|
||||
"help": "Schedule._info.help"
|
||||
},
|
||||
"OnError": {
|
||||
"name": "Schedule.OnError.name",
|
||||
"help": "Schedule.OnError.help",
|
||||
"stop": "stop",
|
||||
"skip": "skip"
|
||||
}
|
||||
},
|
||||
"Choice1": {
|
||||
"_info": {
|
||||
"name": "Choice1._info.name",
|
||||
"help": "Choice1._info.help"
|
||||
},
|
||||
"Location": {
|
||||
"name": "Choice1.Location.name",
|
||||
"help": "Choice1.Location.help",
|
||||
"None": "None",
|
||||
"0": "0",
|
||||
"1": "1",
|
||||
"2": "2",
|
||||
"3": "3",
|
||||
"4": "4",
|
||||
"5": "5",
|
||||
"6": "6",
|
||||
"7": "7",
|
||||
"8": "8",
|
||||
"9": "9"
|
||||
},
|
||||
"Classrooms": {
|
||||
"name": "Choice1.Classrooms.name",
|
||||
"help": "Choice1.Classrooms.help"
|
||||
}
|
||||
},
|
||||
"Choice2": {
|
||||
"_info": {
|
||||
"name": "Choice2._info.name",
|
||||
"help": "Choice2._info.help"
|
||||
},
|
||||
"Location": {
|
||||
"name": "Choice2.Location.name",
|
||||
"help": "Choice2.Location.help",
|
||||
"None": "None",
|
||||
"0": "0",
|
||||
"1": "1",
|
||||
"2": "2",
|
||||
"3": "3",
|
||||
"4": "4",
|
||||
"5": "5",
|
||||
"6": "6",
|
||||
"7": "7",
|
||||
"8": "8",
|
||||
"9": "9"
|
||||
},
|
||||
"Classrooms": {
|
||||
"name": "Choice2.Classrooms.name",
|
||||
"help": "Choice2.Classrooms.help"
|
||||
}
|
||||
},
|
||||
"Choice3": {
|
||||
"_info": {
|
||||
"name": "Choice3._info.name",
|
||||
"help": "Choice3._info.help"
|
||||
},
|
||||
"Location": {
|
||||
"name": "Choice3.Location.name",
|
||||
"help": "Choice3.Location.help",
|
||||
"None": "None",
|
||||
"0": "0",
|
||||
"1": "1",
|
||||
"2": "2",
|
||||
"3": "3",
|
||||
"4": "4",
|
||||
"5": "5",
|
||||
"6": "6",
|
||||
"7": "7",
|
||||
"8": "8",
|
||||
"9": "9"
|
||||
},
|
||||
"Classrooms": {
|
||||
"name": "Choice3.Classrooms.name",
|
||||
"help": "Choice3.Classrooms.help"
|
||||
}
|
||||
},
|
||||
"Choice4": {
|
||||
"_info": {
|
||||
"name": "Choice4._info.name",
|
||||
"help": "Choice4._info.help"
|
||||
},
|
||||
"Location": {
|
||||
"name": "Choice4.Location.name",
|
||||
"help": "Choice4.Location.help",
|
||||
"None": "None",
|
||||
"0": "0",
|
||||
"1": "1",
|
||||
"2": "2",
|
||||
"3": "3",
|
||||
"4": "4",
|
||||
"5": "5",
|
||||
"6": "6",
|
||||
"7": "7",
|
||||
"8": "8",
|
||||
"9": "9"
|
||||
},
|
||||
"Classrooms": {
|
||||
"name": "Choice4.Classrooms.name",
|
||||
"help": "Choice4.Classrooms.help"
|
||||
}
|
||||
},
|
||||
"Choice5": {
|
||||
"_info": {
|
||||
"name": "Choice5._info.name",
|
||||
"help": "Choice5._info.help"
|
||||
},
|
||||
"Location": {
|
||||
"name": "Choice5.Location.name",
|
||||
"help": "Choice5.Location.help",
|
||||
"None": "None",
|
||||
"0": "0",
|
||||
"1": "1",
|
||||
"2": "2",
|
||||
"3": "3",
|
||||
"4": "4",
|
||||
"5": "5",
|
||||
"6": "6",
|
||||
"7": "7",
|
||||
"8": "8",
|
||||
"9": "9"
|
||||
},
|
||||
"Classrooms": {
|
||||
"name": "Choice5.Classrooms.name",
|
||||
"help": "Choice5.Classrooms.help"
|
||||
}
|
||||
},
|
||||
"Bounty": {
|
||||
"_info": {
|
||||
"name": "悬赏通缉设置",
|
||||
@@ -420,6 +585,182 @@
|
||||
"3": "第三位"
|
||||
}
|
||||
},
|
||||
"NormalShop": {
|
||||
"_info": {
|
||||
"name": "普通商店",
|
||||
"help": ""
|
||||
},
|
||||
"Enable": {
|
||||
"name": "启用",
|
||||
"help": ""
|
||||
},
|
||||
"Purchases": {
|
||||
"name": "购买次数",
|
||||
"help": "指定物品购买 X 次。例:\n购买 2 次 = 商店默认 1 次 + 刷新购买 1 次",
|
||||
"1": "1",
|
||||
"2": "2",
|
||||
"3": "3",
|
||||
"4": "4"
|
||||
},
|
||||
"1": {
|
||||
"name": "1",
|
||||
"help": "x5 初级活动报告书 - 12,500 信用点"
|
||||
},
|
||||
"2": {
|
||||
"name": "2",
|
||||
"help": "x5 中级活动报告书 - 125,000 信用点"
|
||||
},
|
||||
"3": {
|
||||
"name": "3",
|
||||
"help": "x3 高级活动报告书 - 300,000 信用点"
|
||||
},
|
||||
"4": {
|
||||
"name": "4",
|
||||
"help": "x1 特级活动报告书 - 500,000 信用点"
|
||||
},
|
||||
"5": {
|
||||
"name": "5",
|
||||
"help": "x5 低级强化珠 - 10,000 信用点"
|
||||
},
|
||||
"6": {
|
||||
"name": "6",
|
||||
"help": "x5 中级强化珠 - 40,000 信用点"
|
||||
},
|
||||
"7": {
|
||||
"name": "7",
|
||||
"help": "x3 高级强化珠 - 96,000 信用点"
|
||||
},
|
||||
"8": {
|
||||
"name": "8",
|
||||
"help": "x1 特级强化珠 - 128,000 信用点"
|
||||
},
|
||||
"9": {
|
||||
"name": "9",
|
||||
"help": "x5 低级强化珠 - 10,000 信用点"
|
||||
},
|
||||
"10": {
|
||||
"name": "10",
|
||||
"help": "x5 中级强化珠 - 40,000 信用点"
|
||||
},
|
||||
"11": {
|
||||
"name": "11",
|
||||
"help": "x3 高级强化珠 - 96,000 信用点"
|
||||
},
|
||||
"12": {
|
||||
"name": "12",
|
||||
"help": "x1 特级强化珠 - 128,000 信用点"
|
||||
},
|
||||
"13": {
|
||||
"name": "13",
|
||||
"help": "x5 低级强化珠 - 10,000 信用点"
|
||||
},
|
||||
"14": {
|
||||
"name": "14",
|
||||
"help": "x5 中级强化珠 - 40,000 信用点"
|
||||
},
|
||||
"15": {
|
||||
"name": "15",
|
||||
"help": "x3 高级强化珠 - 96,000 信用点"
|
||||
},
|
||||
"16": {
|
||||
"name": "16",
|
||||
"help": "x1 特级强化珠 - 128,000 信用点"
|
||||
},
|
||||
"17": {
|
||||
"name": "17",
|
||||
"help": "x1 随机材料 - 8,000 信用点"
|
||||
},
|
||||
"18": {
|
||||
"name": "18",
|
||||
"help": "x1 随机材料 - 8,000 信用点"
|
||||
},
|
||||
"19": {
|
||||
"name": "19",
|
||||
"help": "x1 随机材料 - 8,000 信用点"
|
||||
},
|
||||
"20": {
|
||||
"name": "20",
|
||||
"help": "x1 随机材料 - 8,000 信用点"
|
||||
}
|
||||
},
|
||||
"TacticalChallengeShop": {
|
||||
"_info": {
|
||||
"name": "战术对抗赛商店",
|
||||
"help": ""
|
||||
},
|
||||
"Enable": {
|
||||
"name": "启用",
|
||||
"help": ""
|
||||
},
|
||||
"Purchases": {
|
||||
"name": "购买次数",
|
||||
"help": "指定物品购买 X 次。例:\n购买 2 次 = 商店默认 1 次 + 刷新购买 1 次",
|
||||
"1": "1",
|
||||
"2": "2",
|
||||
"3": "3",
|
||||
"4": "4"
|
||||
},
|
||||
"1": {
|
||||
"name": "1",
|
||||
"help": "x5 静子的神名文字 - 50 战术硬币"
|
||||
},
|
||||
"2": {
|
||||
"name": "2",
|
||||
"help": "x5 真白的神名文字 - 50 战术硬币"
|
||||
},
|
||||
"3": {
|
||||
"name": "3",
|
||||
"help": "x5 纱绫的神名文字 - 50 战术硬币"
|
||||
},
|
||||
"4": {
|
||||
"name": "4",
|
||||
"help": "x5 枫香的神名文字 - 50 战术硬币"
|
||||
},
|
||||
"5": {
|
||||
"name": "5",
|
||||
"help": "x5 歌原的神名文字 - 50 战术硬币"
|
||||
},
|
||||
"6": {
|
||||
"name": "6",
|
||||
"help": "x1 低级能量饮料 (30 体力) - 15 战术硬币"
|
||||
},
|
||||
"7": {
|
||||
"name": "7",
|
||||
"help": "x1 中级能量饮料 (60 体力) - 30 战术硬币"
|
||||
},
|
||||
"8": {
|
||||
"name": "8",
|
||||
"help": "x5 初级活动报告书 - 5 战术硬币"
|
||||
},
|
||||
"9": {
|
||||
"name": "9",
|
||||
"help": "x5 中级活动报告书 - 25 战术硬币"
|
||||
},
|
||||
"10": {
|
||||
"name": "10",
|
||||
"help": "x3 高级活动报告书 - 60 战术硬币"
|
||||
},
|
||||
"11": {
|
||||
"name": "11",
|
||||
"help": "x1 特级活动报告书 - 100 战术硬币"
|
||||
},
|
||||
"12": {
|
||||
"name": "12",
|
||||
"help": "x5000 信用点 - 4 战术硬币"
|
||||
},
|
||||
"13": {
|
||||
"name": "13",
|
||||
"help": "x25k 信用点 - 20 战术硬币"
|
||||
},
|
||||
"14": {
|
||||
"name": "14",
|
||||
"help": "x75k 信用点 - 60 战术硬币"
|
||||
},
|
||||
"15": {
|
||||
"name": "15",
|
||||
"help": "x125k 信用点 - 100 战术硬币"
|
||||
}
|
||||
},
|
||||
"ItemStorage": {
|
||||
"_info": {
|
||||
"name": "ItemStorage._info.name",
|
||||
|
||||