1
0
mirror of https://github.com/TheFunny/ArisuAutoSweeper synced 2026-06-24 20:25:16 +00:00

153 Commits

Author SHA1 Message Date
RedDeadDepresso bf51eb5c5c Merge dd5e6d7803 into 0d66a5c424 2023-12-25 17:52:02 +00:00
RedDeadDepresso dd5e6d7803 feat: auto-generate MCE/config.json 2023-12-25 17:51:56 +00:00
RedDeadDepresso ea2b9d67cd revert changes in task 2023-12-25 16:30:27 +00:00
RedDeadDepresso cf9468e0c5 Update .gitignore 2023-12-25 14:15:50 +00:00
RedDeadDepresso 43074d386c fix: updated regex for mission 2023-12-25 14:07:20 +00:00
RedDeadDepresso 8186513bf1 fix: tasks 2023-12-25 11:48:00 +00:00
RedDeadDepresso 499651f51b feat: mission/commissions/event 2023-12-25 11:35:05 +00:00
YoursFunny 0d66a5c424 fix(tc): change refresh time 2023-12-25 13:56:42 +08:00
YoursFunny 37bbfba299 feat: add tasks assets for jp 2023-12-25 13:17:34 +08:00
YoursFunny 34c5323df3 fix(momotalk): correct sort switch 2023-12-23 17:23:40 +08:00
YoursFunny 30d63fa193 fix(momotalk): correct sidebar switch 2023-12-23 17:03:15 +08:00
YoursFunny be3fdb0988 fix(task): check all claimed 2023-12-23 16:21:46 +08:00
YoursFunny ba9472d0b1 lang: add zh for new settings 2023-12-23 16:09:06 +08:00
RedDeadDepresso 1791b4b05c Added Tasks, Shop, MomoTalk (#11)
* feat: tasks

Added module tasks for EN

* refactor: gui

added tree view Farm and Reward.

* feat: shop

* feat: momotalk

---------

Co-authored-by: YoursFunny <admin@yoursfunny.top>
2023-12-22 13:35:33 +08:00
YoursFunny f8d1ba3d4e perf: change tc priority 2023-12-21 17:56:06 +08:00
YoursFunny 0f6dd93608 fix(sweep): remove index if equal 0 2023-12-09 21:22:11 +08:00
YoursFunny f5cf0a7fbe fix(tc): add handle ui_additional 2023-12-07 18:24:09 +08:00
YoursFunny 16d78e1e16 fix(sweep): correct load index situation 2023-12-07 13:04:05 +08:00
YoursFunny 7b707be841 refactor(scrimmage): use stage ap count 2023-12-06 17:11:50 +08:00
YoursFunny 93bf1f73e2 feat: separate stage ap count 2023-12-05 16:21:19 +08:00
YoursFunny 71da6fd996 feat: add auto select for bounty and scrimmage 2023-11-30 14:21:19 +08:00
YoursFunny ecd4ba0a7c doc: update readme 2023-11-29 18:38:59 +08:00
YoursFunny d684fd79f6 feat(sweep): support finding max sweepable index 2023-11-29 14:55:55 +08:00
YoursFunny 6ae785634c refactor(sweep): separate sweepable check and search_box generate 2023-11-29 14:11:51 +08:00
YoursFunny a0d3fd75af refactor(sweep): change current_indexes 2023-11-29 14:05:50 +08:00
YoursFunny 257e092936 refactor(sweep): use regex for index match 2023-11-29 13:54:55 +08:00
YoursFunny 32cee3f6b5 fix(cafe): adjust name of invitation 2023-11-29 12:39:51 +08:00
YoursFunny 659f58db38 fix(cafe): add check of null invitation name 2023-11-28 21:46:21 +08:00
YoursFunny 6dac8a1de2 feat(cafe): add invitation assets for en 2023-11-28 20:53:24 +08:00
YoursFunny b1aeb64768 lang: change option description 2023-11-28 20:53:01 +08:00
YoursFunny 88e8a98b76 fix(cafe): apply config of choice only when not set before 2023-11-28 16:18:34 +08:00
YoursFunny 39d00ac549 fix(cafe): correct target name check 2023-11-28 16:11:50 +08:00
YoursFunny 11d2a6ef7e feat(cafe): add invitation condition check 2023-11-28 14:04:37 +08:00
YoursFunny 1f5b68d095 feat(webui): add invitation condition options 2023-11-28 14:04:07 +08:00
YoursFunny 5f3ff140dd feat(cafe): add invitation 2023-11-27 22:02:50 +08:00
YoursFunny 8698fa20c2 feat(webui): add config of invitation options 2023-11-27 20:48:37 +08:00
YoursFunny b426c6caac feat(cafe): add invitation assets for jp 2023-11-27 15:44:56 +08:00
YoursFunny 8d83ec1657 fix: add swipe name 2023-11-27 15:26:27 +08:00
YoursFunny 7fcda15329 perf(sweep): improve list insight logic 2023-11-27 13:17:29 +08:00
YoursFunny 54aa1bafb5 refactor(sweep): simplify list enter match 2023-11-26 18:54:26 +08:00
YoursFunny d756b0dc3f perf: reduce config save times 2023-11-25 15:28:57 +08:00
YoursFunny e71118c09e fix: improve data update stability 2023-11-25 15:28:07 +08:00
YoursFunny 44b6d5cdf8 fix: expand ticket ocr region 2023-11-25 14:27:01 +08:00
YoursFunny f92dba92c9 fix(sweep): use swipe instead of drag in list 2023-11-24 14:30:43 +08:00
YoursFunny 86ce04cff9 fix(sweep): extend timer stable 2023-11-24 13:59:39 +08:00
YoursFunny f2065507c2 fix(ui): change task order 2023-11-23 16:37:16 +08:00
YoursFunny 15bf77da3d fix(cafe): expand search box 2023-11-23 16:21:58 +08:00
YoursFunny 4ce8073096 perf(sweep): simplify sweep num ocr 2023-11-23 16:08:51 +08:00
YoursFunny aa872c890d doc: update readme 2023-11-23 14:45:31 +08:00
YoursFunny 25e0559171 feat: add oversea servers 2023-11-23 14:23:52 +08:00
YoursFunny df6da1f77a fix(cafe): adjust property of second cafe setting 2023-11-23 14:06:38 +08:00
YoursFunny fc49adc859 doc: update readme 2023-11-22 22:23:09 +08:00
YoursFunny 4582406ef2 perf(tc): improve status check stability 2023-11-22 22:04:50 +08:00
YoursFunny 256dc96598 fix(tc): restrict count frequency of claim reward 2023-11-22 22:01:07 +08:00
YoursFunny 04744d6f8c fix(scrimmage): add missing multiply of ap count 2023-11-22 22:01:06 +08:00
YoursFunny 8fe578615d fix: change webui port 2023-11-22 21:02:00 +08:00
YoursFunny 5e9615542c fix: set repo when update 2023-11-22 20:47:18 +08:00
YoursFunny 36c5f60eb3 fix(cafe): adjust property of second cafe setting 2023-11-22 19:05:36 +08:00
YoursFunny 91650cc584 feat: add en assets for bounty scrimmage and sweep 2023-11-22 19:04:50 +08:00
YoursFunny 67881568dd perf(button): combine shared assets 2023-11-22 13:13:39 +08:00
YoursFunny f8404edd9e fix: set repo of build-in update 2023-11-21 23:15:06 +08:00
YoursFunny ff3ec041d2 refactor(cafe): separate ui operation and simplify template extraction 2023-11-21 22:18:28 +08:00
YoursFunny 99074a1575 feat(cafe): add template search area 2023-11-21 21:31:01 +08:00
YoursFunny 7862fa6cb8 fix(scrimmage): fix typo 2023-11-21 20:56:39 +08:00
YoursFunny baac90ecf0 fix(resource): remove non-existing assets loading 2023-11-21 20:42:24 +08:00
YoursFunny 53ec298fed fix(alas): validate datetime instead of using regex 2023-11-21 20:29:26 +08:00
YoursFunny b4f18f78ff perf: use more friendly record time on dashboard 2023-11-21 20:27:19 +08:00
YoursFunny eb9af42f38 fix: ignore value in state type args 2023-11-21 20:16:40 +08:00
YoursFunny c29d972c6c feat: support direct_match and match_multi_template 2023-11-21 20:07:45 +08:00
YoursFunny 92b34d4760 perf: release resource when free 2023-11-21 19:59:32 +08:00
YoursFunny 04853b6c31 feat: support load search for buttons 2023-11-21 19:53:02 +08:00
YoursFunny 9604e8962a fix: accept area attr in ClickButton 2023-11-21 19:20:04 +08:00
YoursFunny 03380b2d71 fix: use distinctive search attr for each button frame 2023-11-21 19:17:06 +08:00
YoursFunny c27bd74050 fix: adjust icon css 2023-11-21 19:00:14 +08:00
YoursFunny c3e9945b15 lang: use shorter description for items 2023-11-21 18:43:04 +08:00
YoursFunny 1dd100ac04 lang: fix typo 2023-11-21 15:28:08 +08:00
YoursFunny 77dca70af1 doc: update readme 2023-11-21 15:25:41 +08:00
YoursFunny b8ecd0c9d6 feat: support scrimmage 2023-11-21 15:18:07 +08:00
YoursFunny ceb24283f3 feat: add scrimmage gui option 2023-11-21 15:17:49 +08:00
YoursFunny d0c591af3a fix(bounty): add error handler when enter sweep failed 2023-11-21 14:21:22 +08:00
YoursFunny 589b0b08ec perf(sweep): improve sweep list stability 2023-11-21 14:20:02 +08:00
YoursFunny 299bd6c687 refactor(sweep): change parameter order 2023-11-21 13:54:59 +08:00
YoursFunny e61afaf43b fix(sweep): filter non-digit ocr text 2023-11-21 13:14:30 +08:00
YoursFunny 30e8c8b21b fix(tc): add reward handler 2023-11-21 12:55:16 +08:00
YoursFunny f7b165f589 perf(tc): improve get reward method 2023-11-20 22:34:01 +08:00
YoursFunny 08959e5f1c feat(tc): update storage of tc ticket 2023-11-20 22:26:24 +08:00
YoursFunny e929a1efb1 refactor(tc): separate ui operation 2023-11-20 22:10:40 +08:00
YoursFunny 8e29d7d2c0 feat: add scrimmage config 2023-11-20 21:43:11 +08:00
YoursFunny d3a1a77d6a feat: add scrimmage assets 2023-11-20 21:28:34 +08:00
YoursFunny 930c741de6 doc: update readme 2023-11-20 20:08:26 +08:00
YoursFunny 2c19afdf26 feat(bounty): add error handler choice 2023-11-20 17:28:55 +08:00
YoursFunny bbf3bf7c36 perf(bounty): improve ending logic 2023-11-20 17:06:08 +08:00
YoursFunny 5e4abc147e feat(bounty): detect ticket and end when zero 2023-11-20 16:50:18 +08:00
YoursFunny eb8048ccd6 doc: update readme 2023-11-20 16:31:49 +08:00
YoursFunny 8bec814b8d feat: support bounty 2023-11-20 16:25:06 +08:00
YoursFunny 5fb2810bdc feat: add bounty assets 2023-11-20 16:23:27 +08:00
YoursFunny 8ea95dc340 feat: add bounty gui option 2023-11-20 16:21:40 +08:00
YoursFunny 9d3e581321 feat: add ticket storage in config and gui 2023-11-20 16:20:02 +08:00
YoursFunny d47e463365 perf(sweep): improve sweep flexibility 2023-11-19 23:59:01 +08:00
YoursFunny 0a697e9398 fix: add required argument 2023-11-19 19:41:43 +08:00
YoursFunny 143f519adb fix: remove useless code 2023-11-19 17:05:06 +08:00
YoursFunny af71e797db fix(login): change survey handle for default server 2023-11-19 17:04:40 +08:00
YoursFunny 6ef18ed8c0 feat(login): add survey page handle 2023-11-18 19:16:34 +08:00
YoursFunny 8781e7830c fix(popup): make correction to ap exceed and add insufficient inventory for JP 2023-11-17 14:09:34 +08:00
YoursFunny 84f78230d2 doc: update readme 2023-11-17 13:45:43 +08:00
YoursFunny f506616ba9 feat: add assets for bounty 2023-11-17 13:17:08 +08:00
YoursFunny c1d9ac4f64 feat: add assets for scrimmage (school exchange) 2023-11-17 13:17:08 +08:00
YoursFunny fb28fe297a feat(stage): add sweepable button 2023-11-17 13:17:08 +08:00
YoursFunny 9b17f1948a perf(stage): adjust click interval 2023-11-17 13:17:08 +08:00
YoursFunny e82c4f875c feat(stage): add offset to stage item box 2023-11-17 13:17:07 +08:00
YoursFunny 51ecdf4908 feat(stage): support sweepable detection 2023-11-17 13:17:07 +08:00
YoursFunny 8d2882e752 fix: use correct color space for template matching 2023-11-17 13:17:07 +08:00
YoursFunny 1a66e767f3 refactor(stage): reuse assets for all stages 2023-11-17 13:17:07 +08:00
YoursFunny 04fab819b4 feat(stage): add sweep support 2023-11-17 13:17:07 +08:00
YoursFunny 023972682d refactor: move stage list to its own folder 2023-11-17 13:17:06 +08:00
YoursFunny 2cefb26759 refactor: set indexes as property 2023-11-17 13:17:06 +08:00
YoursFunny 7ed7cabbaa lang: adjust text of second floor 2023-11-17 13:17:06 +08:00
YoursFunny b62e301c55 feat(cafe): detect server before attempt on second floor 2023-11-17 13:17:06 +08:00
YoursFunny 93e8fd1f0c refactor(cafe): rename template button 2023-11-17 13:17:06 +08:00
YoursFunny b9e0cc3026 fix: adapt for BA 2023-11-17 13:17:05 +08:00
RedDeadDepresso 6cbc4f0788 updated buttons 2023-11-17 13:06:09 +08:00
RedDeadDepresso f6736a71df fixed names of some images 2023-11-17 13:06:09 +08:00
RedDeadDepresso 83cc44f7b7 added more popup pages 2023-11-17 13:06:09 +08:00
RedDeadDepresso 75cd7bafc4 added update images 2023-11-17 13:06:09 +08:00
RedDeadDepresso b3daa45c5a added more images for tc and popup 2023-11-17 13:06:09 +08:00
RedDeadDepresso 47e239aee1 mail images added 2023-11-17 13:06:09 +08:00
RedDeadDepresso 21a08f0b7f added images 2023-11-17 13:06:09 +08:00
YoursFunny 7b2c3bb165 feat: add en and Global server options 2023-11-13 16:06:00 +08:00
YoursFunny 2f4d64e5d0 fix: adapt AAS 2023-11-13 15:19:48 +08:00
YoursFunny 9e5fd34b79 fix(cafe): update assets 2023-11-12 17:49:49 +08:00
YoursFunny 5487338dc6 fix(cafe): remove crop as not needed 2023-11-12 17:49:49 +08:00
YoursFunny e5a914e80f fix(cafe): crop template from button image 2023-11-12 17:49:49 +08:00
YoursFunny f21a97e08d fix(cafe): apply mask 2023-11-12 17:49:49 +08:00
YoursFunny b4c47e2de2 feat(stage): add stage list recognition 2023-11-12 17:49:49 +08:00
YoursFunny 34208763aa refactor(cafe): adjust crop template button 2023-11-12 17:49:49 +08:00
YoursFunny c3354cf4f5 lang: adjust en gui text 2023-11-12 17:41:46 +08:00
YoursFunny 81abba0270 doc: fix typo 2023-11-12 16:18:16 +08:00
YoursFunny acea188931 doc: add more relative projects 2023-11-12 01:31:04 +08:00
YoursFunny b0621b48e6 fix(webui): use utf-8 encoding for icon reading 2023-11-12 00:10:43 +08:00
YoursFunny 1b83b7077f doc: add en gui pic 2023-11-10 21:47:14 +08:00
YoursFunny f1e3cd9810 feat(login): add update download confirm 2023-11-10 21:17:20 +08:00
YoursFunny f91fc2c55d feat(popup): add ap exceed and item expire handler 2023-11-10 21:17:20 +08:00
YoursFunny d82e206463 fix(tc): stick at certain status 2023-11-10 21:17:18 +08:00
YoursFunny db813c9efb fix: adapt AAS webui 2023-11-07 19:46:24 +08:00
YoursFunny 9d3b5ceff8 feat: add English webui 2023-11-07 19:43:41 +08:00
YoursFunny b83db6bc35 fix(popup): adjust daily reward recognition 2023-11-07 19:14:42 +08:00
YoursFunny 2e4a1f144b fix(popup): support another type of network reconnection 2023-11-07 19:14:01 +08:00
YoursFunny f7444e29dc doc: add gui pic 2023-11-07 14:58:55 +08:00
YoursFunny 1113094cf5 doc: adjust readme icon style 2023-11-07 14:41:05 +08:00
YoursFunny 60e6710181 doc: add readme 2023-11-07 14:22:47 +08:00
YoursFunny 9708ec05d7 fix(cafe): use correct boarder method 2023-11-06 21:24:13 +08:00
YoursFunny 77f0ded95f feat(cafe): handle unexpected popups 2023-11-05 23:19:51 +08:00
YoursFunny 937a7c63e8 fix(cafe): use smaller default pinch area 2023-11-05 23:09:46 +08:00
376 changed files with 8618 additions and 715 deletions
+1
View File
@@ -20,6 +20,7 @@ config/reloadalas
test.py
test/
note.md
MCE/config.json
# Created by .ignore support plugin (hsz.mobi)
+437
View File
@@ -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\nBD:Commissions EXP\nIR:Commissions Credits\n")
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", "BD", "IR"], 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()
thread = threading.Thread(target=app.check_lock)
thread.start()
app.mainloop()
View File
+95
View File
@@ -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)
+27
View File
@@ -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")
+133
View File
@@ -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
+36
View File
@@ -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)}"
+212
View File
@@ -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)
+448
View File
@@ -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()
Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

+109
View File
@@ -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)
+52
View File
@@ -0,0 +1,52 @@
<img width="150" height="150" align="left" style="float: left; margin: 0 10px 0 0;" alt="AAS icon" src="docs/resources/aas_icon.svg"/>
# ArisuAutoSweeper
**Blue Archive Automation Script**
**| English | [简体中文](README.md) |**
![gui_en.png](docs/resources/gui_en.png)
## Features
The script is still under active development. The following features have been implemented:
- [x] **Cafe** Claim rewards / Interact / Invitation / Second floor
- [x] **Club** Claim AP
- [x] **Mailbox** Claim rewards
- [x] **Bounty** Auto sweep
- [x] **Scrimmage** Auto sweep
- [x] **Tactical Challenge** Claim rewards / Auto battle
Supported servers:
- [x] JP
- [x] OVERSEA
Supported in-game languages:
- [x] Japanese
- [x] English
## Relative projects
- [AzurLaneAutoScript](https://github.com/LmeSzinc/AzurLaneAutoScript): Azur Lane auto script
- [StarRailCopilot](https://github.com/LmeSzinc/StarRailCopilot): A bot for Honkai: Star Rail, based on the next
generation of ALAS framework
Some Blue Archive auto scripts:
- [BAAuto](https://github.com/RedDeadDepresso/BAAuto): Blue Archive Automation Script
- [BlueArchiveAutoScript](https://github.com/pur1fying/blue_archive_auto_script): BAAS, used to implement Blue Archive
automation
- [MBA](https://github.com/MaaAssistantArknights/MBA): BA assistant based on the new architecture of MAA
## Acknowledgements
Thanks to [6bir](https://github.com/6bir) for the icon design.
Thanks to [Alas](https://github.com/LmeSzinc/AzurLaneAutoScript) and [SRC](https://github.com/LmeSzinc/StarRailCopilot)
for the development framework.
Thanks to [RedDeadDepresso](https://github.com/RedDeadDepresso) for EN support.
+59
View File
@@ -0,0 +1,59 @@
<img width="150" height="150" align="left" style="float: left; margin: 0 10px 0 0;" alt="AAS icon" src="docs/resources/aas_icon.svg"/>
# ArisuAutoSweeper
**蔚蓝档案自动化脚本**
**| [English](README.en.md) | 简体中文 |**
![gui_cn.png](docs/resources/gui_cn.png)
## 功能
当前脚本还在活跃开发中,已经实现的功能有:
- [x] **咖啡厅** 领取奖励 / 互动 / 邀请 / 第二咖啡厅
- [x] **公会** 领取体力
- [x] **邮箱** 领取奖励
- [x] **悬赏通缉** 自动扫荡
- [x] **学院交流会** 自动扫荡
- [x] **战术对抗赛** 领取奖励 / 自动战斗
目前支持的服务器:
- [x] 日服
- [x] 国际服
目前支持的游戏内语言:
- [x] 日语
- [x] 英语
## 已知问题
若愿意提供其他语言或国服支持,请开 PR 或 Issue。
- **国际服登录的全屏通知**:未实现自动关闭,正在研究中
- **大小月卡**:未实现自动领取,~~因为没买过~~,可能不影响使用。愿意提供图片的请开 Issue
- **月卡的额外悬赏券和学院交流券**:不太清楚月卡领取额外券的机制,~~因为没买过~~,可能影响相关任务使用券和体力的计算。愿意提供相关信息的请开
Issue
## 相关项目
- [AzurLaneAutoScript](https://github.com/LmeSzinc/AzurLaneAutoScript): 碧蓝航线自动化脚本
- [StarRailCopilot](https://github.com/LmeSzinc/StarRailCopilot): 崩坏:星穹铁道脚本,基于下一代Alas框架
一些蔚蓝档案脚本:
- [BAAuto](https://github.com/RedDeadDepresso/BAAuto): 蔚蓝档案自动脚本
- [BlueArchiveAutoScript](https://github.com/pur1fying/blue_archive_auto_script): BAAS,用于实现蔚蓝档案自动化
- [MBA](https://github.com/MaaAssistantArknights/MBA): 基于 MAA 全新架构的 BA 小助手
## 鸣谢
感谢 [6bir](https://github.com/6bir) 为本项目设计的图标。
感谢 [Alas](https://github.com/LmeSzinc/AzurLaneAutoScript) 以及 [SRC](https://github.com/LmeSzinc/StarRailCopilot)
提供的开发框架。
感谢 [RedDeadDepresso](https://github.com/RedDeadDepresso) 提供英语支持。
+24 -1
View File
@@ -34,15 +34,38 @@ class ArisuAutoSweeper(AzurLaneAutoScript):
from tasks.mail.mail import Mail
Mail(config=self.config, device=self.device).run()
def bounty(self):
from tasks.bounty.bounty import Bounty
Bounty(config=self.config, device=self.device).run()
def scrimmage(self):
from tasks.scrimmage.scrimmage import Scrimmage
Scrimmage(config=self.config, device=self.device).run()
def tactical_challenge(self):
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 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()
Binary file not shown.

After

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.5 KiB

Some files were not shown because too many files have changed in this diff Show More